diff --git a/cli/src/main.rs b/cli/src/main.rs index 7bf013489b..0339becfb4 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -221,50 +221,28 @@ fn run() -> Result<(), Box> { } } } - "vert" => { + ext @ "vert" | ext @ "frag" | ext @ "comp" => { let input = fs::read_to_string(input_path)?; - naga::front::glsl::parse_str( - &input, - &naga::front::glsl::Options { - stage: naga::ShaderStage::Vertex, - defines: Default::default(), - }, - ) - .unwrap_or_else(|err| { - let filename = input_path.file_name().and_then(std::ffi::OsStr::to_str); - emit_glsl_parser_error(err, filename.unwrap_or("glsl"), &input); - std::process::exit(1); - }) - } - "frag" => { - let input = fs::read_to_string(input_path)?; - naga::front::glsl::parse_str( - &input, - &naga::front::glsl::Options { - stage: naga::ShaderStage::Fragment, - defines: Default::default(), - }, - ) - .unwrap_or_else(|err| { - let filename = input_path.file_name().and_then(std::ffi::OsStr::to_str); - emit_glsl_parser_error(err, filename.unwrap_or("glsl"), &input); - std::process::exit(1); - }) - } - "comp" => { - let input = fs::read_to_string(input_path)?; - naga::front::glsl::parse_str( - &input, - &naga::front::glsl::Options { - stage: naga::ShaderStage::Compute, - defines: Default::default(), - }, - ) - .unwrap_or_else(|err| { - let filename = input_path.file_name().and_then(std::ffi::OsStr::to_str); - emit_glsl_parser_error(err, filename.unwrap_or("glsl"), &input); - std::process::exit(1); - }) + let mut parser = naga::front::glsl::Parser::default(); + + parser + .parse( + &naga::front::glsl::Options { + stage: match ext { + "vert" => naga::ShaderStage::Vertex, + "frag" => naga::ShaderStage::Fragment, + "comp" => naga::ShaderStage::Compute, + _ => unreachable!(), + }, + defines: Default::default(), + }, + &input, + ) + .unwrap_or_else(|err| { + let filename = input_path.file_name().and_then(std::ffi::OsStr::to_str); + emit_glsl_parser_error(err, filename.unwrap_or("glsl"), &input); + std::process::exit(1); + }) } _ => return Err(CliError("Unknown input file extension").into()), }; diff --git a/src/front/glsl/ast.rs b/src/front/glsl/ast.rs index 08e2e65229..751fb4d67a 100644 --- a/src/front/glsl/ast.rs +++ b/src/front/glsl/ast.rs @@ -1,17 +1,8 @@ -use super::{ - super::{Emitter, Typifier}, - constants::ConstantSolver, - error::ErrorKind, - SourceMetadata, -}; +use super::SourceMetadata; use crate::{ - proc::ResolveContext, Arena, BinaryOperator, Binding, Block, Constant, Expression, FastHashMap, - Function, FunctionArgument, GlobalVariable, Handle, Interpolation, LocalVariable, Module, - RelationalFunction, ResourceBinding, Sampling, ScalarKind, ScalarValue, ShaderStage, Statement, - StorageAccess, StorageClass, Type, TypeInner, UnaryOperator, VectorSize, + BinaryOperator, Binding, Constant, Expression, Function, GlobalVariable, Handle, Interpolation, + ResourceBinding, Sampling, StorageAccess, StorageClass, Type, UnaryOperator, }; -use core::convert::TryFrom; -use std::ops::Index; #[derive(Debug, Clone, Copy)] pub enum GlobalLookupKind { @@ -55,921 +46,6 @@ pub struct EntryArg { pub storage: StorageQualifier, } -#[derive(Debug)] -pub struct Program { - pub version: u16, - pub profile: Profile, - pub stage: ShaderStage, - - pub workgroup_size: [u32; 3], - pub early_fragment_tests: bool, - - pub lookup_function: FastHashMap>, - pub lookup_type: FastHashMap>, - - pub global_variables: Vec<(String, GlobalLookup)>, - - pub entry_args: Vec, - pub entry_point: Option>, - - pub module: Module, -} - -impl Program { - pub fn new(stage: ShaderStage) -> Program { - Program { - version: 0, - profile: Profile::Core, - stage, - - workgroup_size: [if stage == ShaderStage::Compute { 1 } else { 0 }; 3], - early_fragment_tests: false, - - lookup_function: FastHashMap::default(), - lookup_type: FastHashMap::default(), - global_variables: Vec::new(), - - entry_args: Vec::new(), - entry_point: None, - - module: Module::default(), - } - } - - pub fn typifier_grow( - &self, - context: &mut Context, - handle: Handle, - meta: SourceMetadata, - ) -> Result<(), ErrorKind> { - let resolve_ctx = ResolveContext { - constants: &self.module.constants, - types: &self.module.types, - global_vars: &self.module.global_variables, - local_vars: context.locals, - functions: &self.module.functions, - arguments: context.arguments, - }; - - context - .typifier - .grow(handle, context.expressions, &resolve_ctx) - .map_err(|error| { - ErrorKind::SemanticError(meta, format!("Can't resolve type: {:?}", error).into()) - }) - } - - pub fn resolve_type<'b>( - &'b self, - context: &'b mut Context, - handle: Handle, - meta: SourceMetadata, - ) -> Result<&'b TypeInner, ErrorKind> { - self.typifier_grow(context, handle, meta)?; - Ok(context.typifier.get(handle, &self.module.types)) - } - - /// Invalidates the cached type resolution for `handle` forcing a recomputation - pub fn invalidate_expression<'b>( - &'b self, - context: &'b mut Context, - handle: Handle, - meta: SourceMetadata, - ) -> Result<(), ErrorKind> { - let resolve_ctx = ResolveContext { - constants: &self.module.constants, - types: &self.module.types, - global_vars: &self.module.global_variables, - local_vars: context.locals, - functions: &self.module.functions, - arguments: context.arguments, - }; - - context - .typifier - .invalidate(handle, context.expressions, &resolve_ctx) - .map_err(|error| { - ErrorKind::SemanticError(meta, format!("Can't resolve type: {:?}", error).into()) - }) - } - - pub fn solve_constant( - &mut self, - ctx: &Context, - root: Handle, - meta: SourceMetadata, - ) -> Result, ErrorKind> { - let mut solver = ConstantSolver { - types: &self.module.types, - expressions: ctx.expressions, - constants: &mut self.module.constants, - }; - - solver.solve(root).map_err(|e| (meta, e).into()) - } -} - -#[derive(Debug, PartialEq)] -pub enum Profile { - Core, -} - -#[derive(Debug)] -pub struct Context<'function> { - expressions: &'function mut Arena, - pub locals: &'function mut Arena, - pub arguments: &'function mut Vec, - pub parameters_info: Vec, - - //TODO: Find less allocation heavy representation - pub scopes: Vec>, - pub lookup_global_var_exps: FastHashMap, - pub samplers: FastHashMap, Handle>, - pub typifier: Typifier, - - pub hir_exprs: Arena, - emitter: Emitter, -} - -impl<'function> Context<'function> { - pub fn new( - program: &mut Program, - body: &mut Block, - expressions: &'function mut Arena, - locals: &'function mut Arena, - arguments: &'function mut Vec, - ) -> Self { - let mut this = Context { - expressions, - locals, - arguments, - parameters_info: Vec::new(), - - scopes: vec![FastHashMap::default()], - lookup_global_var_exps: FastHashMap::with_capacity_and_hasher( - program.global_variables.len(), - Default::default(), - ), - typifier: Typifier::new(), - samplers: FastHashMap::default(), - - hir_exprs: Arena::default(), - emitter: Emitter::default(), - }; - - this.emit_start(); - - for &(ref name, lookup) in program.global_variables.iter() { - this.add_global(name, lookup, program, body) - } - - this - } - - pub fn add_global( - &mut self, - name: &str, - GlobalLookup { - kind, - entry_arg, - mutable, - }: GlobalLookup, - program: &Program, - body: &mut Block, - ) { - self.emit_flush(body); - let (expr, load) = match kind { - GlobalLookupKind::Variable(v) => { - let res = ( - self.expressions.append(Expression::GlobalVariable(v)), - program.module.global_variables[v].class != StorageClass::Handle, - ); - self.emit_start(); - - res - } - GlobalLookupKind::BlockSelect(handle, index) => { - let base = self.expressions.append(Expression::GlobalVariable(handle)); - self.emit_start(); - let expr = self - .expressions - .append(Expression::AccessIndex { base, index }); - - (expr, { - let ty = program.module.global_variables[handle].ty; - - match program.module.types[ty].inner { - TypeInner::Struct { ref members, .. } => { - if let TypeInner::Array { - size: crate::ArraySize::Dynamic, - .. - } = program.module.types[members[index as usize].ty].inner - { - false - } else { - true - } - } - _ => true, - } - }) - } - GlobalLookupKind::Constant(v) => { - let res = (self.expressions.append(Expression::Constant(v)), false); - self.emit_start(); - res - } - }; - - let var = VariableReference { - expr, - load, - mutable, - entry_arg, - }; - - self.lookup_global_var_exps.insert(name.into(), var); - } - - pub fn emit_start(&mut self) { - self.emitter.start(self.expressions) - } - - pub fn emit_flush(&mut self, body: &mut Block) { - body.extend(self.emitter.finish(self.expressions)) - } - - pub fn add_expression(&mut self, expr: Expression, body: &mut Block) -> Handle { - if expr.needs_pre_emit() { - self.emit_flush(body); - let handle = self.expressions.append(expr); - self.emit_start(); - handle - } else { - self.expressions.append(expr) - } - } - - pub fn lookup_local_var(&self, name: &str) -> Option { - for scope in self.scopes.iter().rev() { - if let Some(var) = scope.get(name) { - return Some(var.clone()); - } - } - None - } - - pub fn lookup_global_var(&mut self, name: &str) -> Option { - self.lookup_global_var_exps.get(name).cloned() - } - - #[cfg(feature = "glsl-validate")] - pub fn lookup_local_var_current_scope(&self, name: &str) -> Option { - if let Some(current) = self.scopes.last() { - current.get(name).cloned() - } else { - None - } - } - - /// Add variable to current scope - pub fn add_local_var(&mut self, name: String, expr: Handle, mutable: bool) { - if let Some(current) = self.scopes.last_mut() { - (*current).insert( - name, - VariableReference { - expr, - load: true, - mutable, - entry_arg: None, - }, - ); - } - } - - /// Add function argument to current scope - pub fn add_function_arg( - &mut self, - program: &mut Program, - parameters: &mut Vec>, - body: &mut Block, - name: Option, - ty: Handle, - qualifier: ParameterQualifier, - ) { - let index = self.arguments.len(); - let mut arg = FunctionArgument { - name: name.clone(), - ty, - binding: None, - }; - parameters.push(ty); - - let opaque = match program.module.types[ty].inner { - TypeInner::Image { .. } | TypeInner::Sampler { .. } => true, - _ => false, - }; - - if qualifier.is_lhs() { - arg.ty = program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Pointer { - base: arg.ty, - class: StorageClass::Function, - }, - }) - } - - self.arguments.push(arg); - - self.parameters_info.push(ParameterInfo { - qualifier, - depth: false, - }); - - if let Some(name) = name { - let expr = self.add_expression(Expression::FunctionArgument(index as u32), body); - let mutable = qualifier != ParameterQualifier::Const && !opaque; - let load = qualifier.is_lhs(); - - if mutable && !load { - let handle = self.locals.append(LocalVariable { - name: Some(name.clone()), - ty, - init: None, - }); - let local_expr = self.add_expression(Expression::LocalVariable(handle), body); - - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { - pointer: local_expr, - value: expr, - }); - - if let Some(current) = self.scopes.last_mut() { - (*current).insert( - name, - VariableReference { - expr: local_expr, - load: true, - mutable, - entry_arg: None, - }, - ); - } - } else if let Some(current) = self.scopes.last_mut() { - (*current).insert( - name, - VariableReference { - expr, - load, - mutable, - entry_arg: None, - }, - ); - } - } - } - - /// Add new empty scope - pub fn push_scope(&mut self) { - self.scopes.push(FastHashMap::default()); - } - - pub fn remove_current_scope(&mut self) { - self.scopes.pop(); - } - - pub fn lower_expect( - &mut self, - program: &mut Program, - expr: Handle, - lhs: bool, - body: &mut Block, - ) -> Result<(Handle, SourceMetadata), ErrorKind> { - let (maybe_expr, meta) = self.lower(program, expr, lhs, body)?; - - let expr = match maybe_expr { - Some(e) => e, - None => { - return Err(ErrorKind::SemanticError( - meta, - "Expression returns void".into(), - )) - } - }; - - Ok((expr, meta)) - } - - pub fn lower( - &mut self, - program: &mut Program, - expr: Handle, - lhs: bool, - body: &mut Block, - ) -> Result<(Option>, SourceMetadata), ErrorKind> { - let HirExpr { kind, meta } = self.hir_exprs[expr].clone(); - - let handle = match kind { - HirExprKind::Access { base, index } => { - let base = self.lower_expect(program, base, true, body)?.0; - let (index, index_meta) = self.lower_expect(program, index, false, body)?; - - let pointer = program - .solve_constant(self, index, index_meta) - .ok() - .and_then(|constant| { - Some(self.add_expression( - Expression::AccessIndex { - base, - index: match program.module.constants[constant].inner { - crate::ConstantInner::Scalar { - value: ScalarValue::Uint(i), - .. - } => u32::try_from(i).ok()?, - crate::ConstantInner::Scalar { - value: ScalarValue::Sint(i), - .. - } => u32::try_from(i).ok()?, - _ => return None, - }, - }, - body, - )) - }) - .unwrap_or_else(|| { - self.add_expression(Expression::Access { base, index }, body) - }); - - if !lhs { - let resolved = program.resolve_type(self, pointer, meta)?; - if resolved.pointer_class().is_some() { - return Ok(( - Some(self.add_expression(Expression::Load { pointer }, body)), - meta, - )); - } - } - - pointer - } - HirExprKind::Select { base, field } => { - let base = self.lower_expect(program, base, lhs, body)?.0; - - program.field_selection(self, lhs, body, base, &field, meta)? - } - HirExprKind::Constant(constant) if !lhs => { - self.add_expression(Expression::Constant(constant), body) - } - HirExprKind::Binary { left, op, right } if !lhs => { - let (mut left, left_meta) = self.lower_expect(program, left, false, body)?; - let (mut right, right_meta) = self.lower_expect(program, right, false, body)?; - - match op { - BinaryOperator::ShiftLeft | BinaryOperator::ShiftRight => self - .implicit_conversion( - program, - &mut right, - right_meta, - ScalarKind::Uint, - 4, - )?, - _ => self.binary_implicit_conversion( - program, &mut left, left_meta, &mut right, right_meta, - )?, - } - - program.typifier_grow(self, left, left_meta)?; - program.typifier_grow(self, right, right_meta)?; - - let left_inner = self.typifier.get(left, &program.module.types); - let right_inner = self.typifier.get(right, &program.module.types); - - match (left_inner, right_inner) { - (&TypeInner::Vector { .. }, &TypeInner::Vector { .. }) - | (&TypeInner::Matrix { .. }, &TypeInner::Matrix { .. }) => match op { - BinaryOperator::Equal | BinaryOperator::NotEqual => { - let equals = op == BinaryOperator::Equal; - - let (op, fun) = match equals { - true => (BinaryOperator::Equal, RelationalFunction::All), - false => (BinaryOperator::NotEqual, RelationalFunction::Any), - }; - - let argument = - self.expressions - .append(Expression::Binary { op, left, right }); - - self.add_expression(Expression::Relational { fun, argument }, body) - } - _ => self.add_expression(Expression::Binary { left, op, right }, body), - }, - (&TypeInner::Vector { size, .. }, &TypeInner::Scalar { .. }) => match op { - BinaryOperator::Add - | BinaryOperator::Subtract - | BinaryOperator::Divide - | BinaryOperator::ShiftLeft - | BinaryOperator::ShiftRight => { - let scalar_vector = - self.add_expression(Expression::Splat { size, value: right }, body); - - self.add_expression( - Expression::Binary { - op, - left, - right: scalar_vector, - }, - body, - ) - } - _ => self.add_expression(Expression::Binary { left, op, right }, body), - }, - (&TypeInner::Scalar { .. }, &TypeInner::Vector { size, .. }) => match op { - BinaryOperator::Add | BinaryOperator::Subtract | BinaryOperator::Divide => { - let scalar_vector = - self.add_expression(Expression::Splat { size, value: left }, body); - - self.add_expression( - Expression::Binary { - op, - left: scalar_vector, - right, - }, - body, - ) - } - _ => self.add_expression(Expression::Binary { left, op, right }, body), - }, - _ => self.add_expression(Expression::Binary { left, op, right }, body), - } - } - HirExprKind::Unary { op, expr } if !lhs => { - let expr = self.lower_expect(program, expr, false, body)?.0; - - self.add_expression(Expression::Unary { op, expr }, body) - } - HirExprKind::Variable(var) => { - if lhs { - if !var.mutable { - return Err(ErrorKind::SemanticError( - meta, - "Variable cannot be used in LHS position".into(), - )); - } - - var.expr - } else if var.load { - self.add_expression(Expression::Load { pointer: var.expr }, body) - } else { - var.expr - } - } - HirExprKind::Call(call) if !lhs => { - let maybe_expr = program - .function_or_constructor_call(self, body, call.kind, &call.args, meta)?; - return Ok((maybe_expr, meta)); - } - HirExprKind::Conditional { - condition, - accept, - reject, - } if !lhs => { - let condition = self.lower_expect(program, condition, false, body)?.0; - let (mut accept, accept_meta) = self.lower_expect(program, accept, false, body)?; - let (mut reject, reject_meta) = self.lower_expect(program, reject, false, body)?; - - self.binary_implicit_conversion( - program, - &mut accept, - accept_meta, - &mut reject, - reject_meta, - )?; - - self.add_expression( - Expression::Select { - condition, - accept, - reject, - }, - body, - ) - } - HirExprKind::Assign { tgt, value } if !lhs => { - let (pointer, ptr_meta) = self.lower_expect(program, tgt, true, body)?; - let (mut value, value_meta) = self.lower_expect(program, value, false, body)?; - - let scalar_components = self.expr_scalar_components(program, pointer, ptr_meta)?; - - if let Some((kind, width)) = scalar_components { - self.implicit_conversion(program, &mut value, value_meta, kind, width)?; - } - - if let Expression::Swizzle { - size, - mut vector, - pattern, - } = self.expressions[pointer] - { - // Stores to swizzled values are not directly supported, - // lower them as series of per-component stores. - let size = match size { - VectorSize::Bi => 2, - VectorSize::Tri => 3, - VectorSize::Quad => 4, - }; - - if let Expression::Load { pointer } = self.expressions[vector] { - vector = pointer; - } - - #[allow(clippy::needless_range_loop)] - for index in 0..size { - let dst = self.add_expression( - Expression::AccessIndex { - base: vector, - index: pattern[index].index(), - }, - body, - ); - let src = self.add_expression( - Expression::AccessIndex { - base: value, - index: index as u32, - }, - body, - ); - - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { - pointer: dst, - value: src, - }); - } - } else { - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { pointer, value }); - } - - value - } - HirExprKind::IncDec { - increment, - postfix, - expr, - } => { - let op = match increment { - true => BinaryOperator::Add, - false => BinaryOperator::Subtract, - }; - - let pointer = self.lower_expect(program, expr, true, body)?.0; - let left = self.add_expression(Expression::Load { pointer }, body); - - let uint = if let Some(kind) = program.resolve_type(self, left, meta)?.scalar_kind() - { - match kind { - ScalarKind::Sint => false, - ScalarKind::Uint => true, - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Increment/decrement operations must operate in integers".into(), - )) - } - } - } else { - return Err(ErrorKind::SemanticError( - meta, - "Increment/decrement operations must operate in integers".into(), - )); - }; - - let one = program.module.constants.append(Constant { - name: None, - specialization: None, - inner: crate::ConstantInner::Scalar { - width: 4, - value: match uint { - true => crate::ScalarValue::Uint(1), - false => crate::ScalarValue::Sint(1), - }, - }, - }); - let right = self.add_expression(Expression::Constant(one), body); - - let value = self.add_expression(Expression::Binary { op, left, right }, body); - - if postfix { - let local = self.locals.append(LocalVariable { - name: None, - ty: program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Scalar { - kind: match uint { - true => ScalarKind::Uint, - false => ScalarKind::Sint, - }, - width: 4, - }, - }), - init: None, - }); - - let expr = self.add_expression(Expression::LocalVariable(local), body); - let load = self.add_expression(Expression::Load { pointer: expr }, body); - - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { - pointer: expr, - value: left, - }); - - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { pointer, value }); - - load - } else { - self.emit_flush(body); - self.emit_start(); - - body.push(Statement::Store { pointer, value }); - - left - } - } - _ => { - return Err(ErrorKind::SemanticError( - meta, - format!("{:?} cannot be in the left hand side", self.hir_exprs[expr]).into(), - )) - } - }; - - Ok((Some(handle), meta)) - } - - pub fn expr_scalar_components( - &mut self, - program: &mut Program, - expr: Handle, - meta: SourceMetadata, - ) -> Result, ErrorKind> { - let ty = program.resolve_type(self, expr, meta)?; - Ok(scalar_components(ty)) - } - - pub fn expr_power( - &mut self, - program: &mut Program, - expr: Handle, - meta: SourceMetadata, - ) -> Result, ErrorKind> { - Ok(self - .expr_scalar_components(program, expr, meta)? - .and_then(|(kind, _)| type_power(kind))) - } - - pub fn get_expression(&self, expr: Handle) -> &Expression { - &self.expressions[expr] - } - - pub fn implicit_conversion( - &mut self, - program: &mut Program, - expr: &mut Handle, - meta: SourceMetadata, - kind: ScalarKind, - width: crate::Bytes, - ) -> Result<(), ErrorKind> { - if let (Some(tgt_power), Some(expr_power)) = - (type_power(kind), self.expr_power(program, *expr, meta)?) - { - if tgt_power > expr_power { - *expr = self.expressions.append(Expression::As { - expr: *expr, - kind, - convert: Some(width), - }) - } - } - - Ok(()) - } - - pub fn binary_implicit_conversion( - &mut self, - program: &mut Program, - left: &mut Handle, - left_meta: SourceMetadata, - right: &mut Handle, - right_meta: SourceMetadata, - ) -> Result<(), ErrorKind> { - let left_components = self.expr_scalar_components(program, *left, left_meta)?; - let right_components = self.expr_scalar_components(program, *right, right_meta)?; - - if let ( - Some((left_power, left_width, left_kind)), - Some((right_power, right_width, right_kind)), - ) = ( - left_components.and_then(|(kind, width)| Some((type_power(kind)?, width, kind))), - right_components.and_then(|(kind, width)| Some((type_power(kind)?, width, kind))), - ) { - match left_power.cmp(&right_power) { - std::cmp::Ordering::Less => { - *left = self.expressions.append(Expression::As { - expr: *left, - kind: right_kind, - convert: Some(right_width), - }) - } - std::cmp::Ordering::Equal => {} - std::cmp::Ordering::Greater => { - *right = self.expressions.append(Expression::As { - expr: *right, - kind: left_kind, - convert: Some(left_width), - }) - } - } - } - - Ok(()) - } - - pub fn implicit_splat( - &mut self, - program: &mut Program, - expr: &mut Handle, - meta: SourceMetadata, - vector_size: Option, - ) -> Result<(), ErrorKind> { - let expr_type = program.resolve_type(self, *expr, meta)?; - - if let (&TypeInner::Scalar { .. }, Some(size)) = (expr_type, vector_size) { - *expr = self - .expressions - .append(Expression::Splat { size, value: *expr }) - } - - Ok(()) - } - - pub fn vector_resize( - &mut self, - size: VectorSize, - vector: Handle, - body: &mut Block, - ) -> Handle { - self.add_expression( - Expression::Swizzle { - size, - vector, - pattern: crate::SwizzleComponent::XYZW, - }, - body, - ) - } -} - -impl<'function> Index> for Context<'function> { - type Output = Expression; - - fn index(&self, index: Handle) -> &Self::Output { - &self.expressions[index] - } -} - -pub fn scalar_components(ty: &TypeInner) -> Option<(ScalarKind, crate::Bytes)> { - match *ty { - TypeInner::Scalar { kind, width } => Some((kind, width)), - TypeInner::Vector { kind, width, .. } => Some((kind, width)), - TypeInner::Matrix { width, .. } => Some((ScalarKind::Float, width)), - TypeInner::ValuePointer { kind, width, .. } => Some((kind, width)), - _ => None, - } -} - -pub fn type_power(kind: ScalarKind) -> Option { - Some(match kind { - ScalarKind::Sint => 0, - ScalarKind::Uint => 1, - ScalarKind::Float => 2, - ScalarKind::Bool => return None, - }) -} - #[derive(Debug, Clone)] pub struct VariableReference { pub expr: Handle, @@ -1087,3 +163,8 @@ impl ParameterQualifier { } } } + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Profile { + Core, +} diff --git a/src/front/glsl/context.rs b/src/front/glsl/context.rs new file mode 100644 index 0000000000..b756bb08da --- /dev/null +++ b/src/front/glsl/context.rs @@ -0,0 +1,787 @@ +use crate::{ + front::{ + glsl::{ + ast::{ + GlobalLookup, GlobalLookupKind, HirExpr, HirExprKind, ParameterInfo, + ParameterQualifier, VariableReference, + }, + error::ErrorKind, + types::{scalar_components, type_power}, + Parser, SourceMetadata, + }, + Emitter, Typifier, + }, + Arena, BinaryOperator, Block, Constant, Expression, FastHashMap, FunctionArgument, Handle, + LocalVariable, RelationalFunction, ScalarKind, ScalarValue, Statement, StorageClass, Type, + TypeInner, VectorSize, +}; +use std::{convert::TryFrom, ops::Index}; + +#[derive(Debug)] +pub struct Context { + pub expressions: Arena, + pub locals: Arena, + pub arguments: Vec, + + pub parameters: Vec>, + pub parameters_info: Vec, + + //TODO: Find less allocation heavy representation + pub scopes: Vec>, + pub lookup_global_var_exps: FastHashMap, + pub samplers: FastHashMap, Handle>, + + pub hir_exprs: Arena, + pub typifier: Typifier, + emitter: Emitter, +} + +impl Context { + pub fn new(parser: &Parser, body: &mut Block) -> Self { + let mut this = Context { + expressions: Arena::new(), + locals: Arena::new(), + arguments: Vec::new(), + + parameters: Vec::new(), + parameters_info: Vec::new(), + + scopes: vec![FastHashMap::default()], + lookup_global_var_exps: FastHashMap::with_capacity_and_hasher( + parser.global_variables.len(), + Default::default(), + ), + samplers: FastHashMap::default(), + + hir_exprs: Arena::default(), + typifier: Typifier::new(), + emitter: Emitter::default(), + }; + + this.emit_start(); + + for &(ref name, lookup) in parser.global_variables.iter() { + this.add_global(parser, name, lookup, body) + } + + this + } + + pub fn add_global( + &mut self, + parser: &Parser, + name: &str, + GlobalLookup { + kind, + entry_arg, + mutable, + }: GlobalLookup, + body: &mut Block, + ) { + self.emit_flush(body); + let (expr, load) = match kind { + GlobalLookupKind::Variable(v) => { + let res = ( + self.expressions.append(Expression::GlobalVariable(v)), + parser.module.global_variables[v].class != StorageClass::Handle, + ); + self.emit_start(); + + res + } + GlobalLookupKind::BlockSelect(handle, index) => { + let base = self.expressions.append(Expression::GlobalVariable(handle)); + self.emit_start(); + let expr = self + .expressions + .append(Expression::AccessIndex { base, index }); + + (expr, { + let ty = parser.module.global_variables[handle].ty; + + match parser.module.types[ty].inner { + TypeInner::Struct { ref members, .. } => { + if let TypeInner::Array { + size: crate::ArraySize::Dynamic, + .. + } = parser.module.types[members[index as usize].ty].inner + { + false + } else { + true + } + } + _ => true, + } + }) + } + GlobalLookupKind::Constant(v) => { + let res = (self.expressions.append(Expression::Constant(v)), false); + self.emit_start(); + res + } + }; + + let var = VariableReference { + expr, + load, + mutable, + entry_arg, + }; + + self.lookup_global_var_exps.insert(name.into(), var); + } + + pub fn emit_start(&mut self) { + self.emitter.start(&self.expressions) + } + + pub fn emit_flush(&mut self, body: &mut Block) { + body.extend(self.emitter.finish(&self.expressions)) + } + + pub fn add_expression(&mut self, expr: Expression, body: &mut Block) -> Handle { + if expr.needs_pre_emit() { + self.emit_flush(body); + let handle = self.expressions.append(expr); + self.emit_start(); + handle + } else { + self.expressions.append(expr) + } + } + + pub fn lookup_local_var(&self, name: &str) -> Option { + for scope in self.scopes.iter().rev() { + if let Some(var) = scope.get(name) { + return Some(var.clone()); + } + } + None + } + + pub fn lookup_global_var(&mut self, name: &str) -> Option { + self.lookup_global_var_exps.get(name).cloned() + } + + #[cfg(feature = "glsl-validate")] + pub fn lookup_local_var_current_scope(&self, name: &str) -> Option { + if let Some(current) = self.scopes.last() { + current.get(name).cloned() + } else { + None + } + } + + /// Add variable to current scope + pub fn add_local_var(&mut self, name: String, expr: Handle, mutable: bool) { + if let Some(current) = self.scopes.last_mut() { + (*current).insert( + name, + VariableReference { + expr, + load: true, + mutable, + entry_arg: None, + }, + ); + } + } + + /// Add function argument to current scope + pub fn add_function_arg( + &mut self, + parser: &mut Parser, + body: &mut Block, + name: Option, + ty: Handle, + qualifier: ParameterQualifier, + ) { + let index = self.arguments.len(); + let mut arg = FunctionArgument { + name: name.clone(), + ty, + binding: None, + }; + self.parameters.push(ty); + + let opaque = match parser.module.types[ty].inner { + TypeInner::Image { .. } | TypeInner::Sampler { .. } => true, + _ => false, + }; + + if qualifier.is_lhs() { + arg.ty = parser.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Pointer { + base: arg.ty, + class: StorageClass::Function, + }, + }) + } + + self.arguments.push(arg); + + self.parameters_info.push(ParameterInfo { + qualifier, + depth: false, + }); + + if let Some(name) = name { + let expr = self.add_expression(Expression::FunctionArgument(index as u32), body); + let mutable = qualifier != ParameterQualifier::Const && !opaque; + let load = qualifier.is_lhs(); + + if mutable && !load { + let handle = self.locals.append(LocalVariable { + name: Some(name.clone()), + ty, + init: None, + }); + let local_expr = self.add_expression(Expression::LocalVariable(handle), body); + + self.emit_flush(body); + self.emit_start(); + + body.push(Statement::Store { + pointer: local_expr, + value: expr, + }); + + if let Some(current) = self.scopes.last_mut() { + (*current).insert( + name, + VariableReference { + expr: local_expr, + load: true, + mutable, + entry_arg: None, + }, + ); + } + } else if let Some(current) = self.scopes.last_mut() { + (*current).insert( + name, + VariableReference { + expr, + load, + mutable, + entry_arg: None, + }, + ); + } + } + } + + /// Add new empty scope + pub fn push_scope(&mut self) { + self.scopes.push(FastHashMap::default()); + } + + pub fn remove_current_scope(&mut self) { + self.scopes.pop(); + } + + pub fn lower_expect( + &mut self, + parser: &mut Parser, + expr: Handle, + lhs: bool, + body: &mut Block, + ) -> Result<(Handle, SourceMetadata), ErrorKind> { + let (maybe_expr, meta) = self.lower(parser, expr, lhs, body)?; + + let expr = match maybe_expr { + Some(e) => e, + None => { + return Err(ErrorKind::SemanticError( + meta, + "Expression returns void".into(), + )) + } + }; + + Ok((expr, meta)) + } + + pub fn lower( + &mut self, + parser: &mut Parser, + expr: Handle, + lhs: bool, + body: &mut Block, + ) -> Result<(Option>, SourceMetadata), ErrorKind> { + let HirExpr { kind, meta } = self.hir_exprs[expr].clone(); + + let handle = match kind { + HirExprKind::Access { base, index } => { + let base = self.lower_expect(parser, base, true, body)?.0; + let (index, index_meta) = self.lower_expect(parser, index, false, body)?; + + let pointer = parser + .solve_constant(self, index, index_meta) + .ok() + .and_then(|constant| { + Some(self.add_expression( + Expression::AccessIndex { + base, + index: match parser.module.constants[constant].inner { + crate::ConstantInner::Scalar { + value: ScalarValue::Uint(i), + .. + } => u32::try_from(i).ok()?, + crate::ConstantInner::Scalar { + value: ScalarValue::Sint(i), + .. + } => u32::try_from(i).ok()?, + _ => return None, + }, + }, + body, + )) + }) + .unwrap_or_else(|| { + self.add_expression(Expression::Access { base, index }, body) + }); + + if !lhs { + let resolved = parser.resolve_type(self, pointer, meta)?; + if resolved.pointer_class().is_some() { + return Ok(( + Some(self.add_expression(Expression::Load { pointer }, body)), + meta, + )); + } + } + + pointer + } + HirExprKind::Select { base, field } => { + let base = self.lower_expect(parser, base, lhs, body)?.0; + + parser.field_selection(self, lhs, body, base, &field, meta)? + } + HirExprKind::Constant(constant) if !lhs => { + self.add_expression(Expression::Constant(constant), body) + } + HirExprKind::Binary { left, op, right } if !lhs => { + let (mut left, left_meta) = self.lower_expect(parser, left, false, body)?; + let (mut right, right_meta) = self.lower_expect(parser, right, false, body)?; + + match op { + BinaryOperator::ShiftLeft | BinaryOperator::ShiftRight => self + .implicit_conversion(parser, &mut right, right_meta, ScalarKind::Uint, 4)?, + _ => self.binary_implicit_conversion( + parser, &mut left, left_meta, &mut right, right_meta, + )?, + } + + parser.typifier_grow(self, left, left_meta)?; + parser.typifier_grow(self, right, right_meta)?; + + let left_inner = self.typifier.get(left, &parser.module.types); + let right_inner = self.typifier.get(right, &parser.module.types); + + match (left_inner, right_inner) { + (&TypeInner::Vector { .. }, &TypeInner::Vector { .. }) + | (&TypeInner::Matrix { .. }, &TypeInner::Matrix { .. }) => match op { + BinaryOperator::Equal | BinaryOperator::NotEqual => { + let equals = op == BinaryOperator::Equal; + + let (op, fun) = match equals { + true => (BinaryOperator::Equal, RelationalFunction::All), + false => (BinaryOperator::NotEqual, RelationalFunction::Any), + }; + + let argument = + self.expressions + .append(Expression::Binary { op, left, right }); + + self.add_expression(Expression::Relational { fun, argument }, body) + } + _ => self.add_expression(Expression::Binary { left, op, right }, body), + }, + (&TypeInner::Vector { size, .. }, &TypeInner::Scalar { .. }) => match op { + BinaryOperator::Add + | BinaryOperator::Subtract + | BinaryOperator::Divide + | BinaryOperator::ShiftLeft + | BinaryOperator::ShiftRight => { + let scalar_vector = + self.add_expression(Expression::Splat { size, value: right }, body); + + self.add_expression( + Expression::Binary { + op, + left, + right: scalar_vector, + }, + body, + ) + } + _ => self.add_expression(Expression::Binary { left, op, right }, body), + }, + (&TypeInner::Scalar { .. }, &TypeInner::Vector { size, .. }) => match op { + BinaryOperator::Add | BinaryOperator::Subtract | BinaryOperator::Divide => { + let scalar_vector = + self.add_expression(Expression::Splat { size, value: left }, body); + + self.add_expression( + Expression::Binary { + op, + left: scalar_vector, + right, + }, + body, + ) + } + _ => self.add_expression(Expression::Binary { left, op, right }, body), + }, + _ => self.add_expression(Expression::Binary { left, op, right }, body), + } + } + HirExprKind::Unary { op, expr } if !lhs => { + let expr = self.lower_expect(parser, expr, false, body)?.0; + + self.add_expression(Expression::Unary { op, expr }, body) + } + HirExprKind::Variable(var) => { + if lhs { + if !var.mutable { + return Err(ErrorKind::SemanticError( + meta, + "Variable cannot be used in LHS position".into(), + )); + } + + var.expr + } else if var.load { + self.add_expression(Expression::Load { pointer: var.expr }, body) + } else { + var.expr + } + } + HirExprKind::Call(call) if !lhs => { + let maybe_expr = + parser.function_or_constructor_call(self, body, call.kind, &call.args, meta)?; + return Ok((maybe_expr, meta)); + } + HirExprKind::Conditional { + condition, + accept, + reject, + } if !lhs => { + let condition = self.lower_expect(parser, condition, false, body)?.0; + let (mut accept, accept_meta) = self.lower_expect(parser, accept, false, body)?; + let (mut reject, reject_meta) = self.lower_expect(parser, reject, false, body)?; + + self.binary_implicit_conversion( + parser, + &mut accept, + accept_meta, + &mut reject, + reject_meta, + )?; + + self.add_expression( + Expression::Select { + condition, + accept, + reject, + }, + body, + ) + } + HirExprKind::Assign { tgt, value } if !lhs => { + let (pointer, ptr_meta) = self.lower_expect(parser, tgt, true, body)?; + let (mut value, value_meta) = self.lower_expect(parser, value, false, body)?; + + let scalar_components = self.expr_scalar_components(parser, pointer, ptr_meta)?; + + if let Some((kind, width)) = scalar_components { + self.implicit_conversion(parser, &mut value, value_meta, kind, width)?; + } + + if let Expression::Swizzle { + size, + mut vector, + pattern, + } = self.expressions[pointer] + { + // Stores to swizzled values are not directly supported, + // lower them as series of per-component stores. + let size = match size { + VectorSize::Bi => 2, + VectorSize::Tri => 3, + VectorSize::Quad => 4, + }; + + if let Expression::Load { pointer } = self.expressions[vector] { + vector = pointer; + } + + #[allow(clippy::needless_range_loop)] + for index in 0..size { + let dst = self.add_expression( + Expression::AccessIndex { + base: vector, + index: pattern[index].index(), + }, + body, + ); + let src = self.add_expression( + Expression::AccessIndex { + base: value, + index: index as u32, + }, + body, + ); + + self.emit_flush(body); + self.emit_start(); + + body.push(Statement::Store { + pointer: dst, + value: src, + }); + } + } else { + self.emit_flush(body); + self.emit_start(); + + body.push(Statement::Store { pointer, value }); + } + + value + } + HirExprKind::IncDec { + increment, + postfix, + expr, + } => { + let op = match increment { + true => BinaryOperator::Add, + false => BinaryOperator::Subtract, + }; + + let pointer = self.lower_expect(parser, expr, true, body)?.0; + let left = self.add_expression(Expression::Load { pointer }, body); + + let uint = if let Some(kind) = parser.resolve_type(self, left, meta)?.scalar_kind() + { + match kind { + ScalarKind::Sint => false, + ScalarKind::Uint => true, + _ => { + return Err(ErrorKind::SemanticError( + meta, + "Increment/decrement operations must operate in integers".into(), + )) + } + } + } else { + return Err(ErrorKind::SemanticError( + meta, + "Increment/decrement operations must operate in integers".into(), + )); + }; + + let one = parser.module.constants.append(Constant { + name: None, + specialization: None, + inner: crate::ConstantInner::Scalar { + width: 4, + value: match uint { + true => crate::ScalarValue::Uint(1), + false => crate::ScalarValue::Sint(1), + }, + }, + }); + let right = self.add_expression(Expression::Constant(one), body); + + let value = self.add_expression(Expression::Binary { op, left, right }, body); + + if postfix { + let local = self.locals.append(LocalVariable { + name: None, + ty: parser.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Scalar { + kind: match uint { + true => ScalarKind::Uint, + false => ScalarKind::Sint, + }, + width: 4, + }, + }), + init: None, + }); + + let expr = self.add_expression(Expression::LocalVariable(local), body); + let load = self.add_expression(Expression::Load { pointer: expr }, body); + + self.emit_flush(body); + self.emit_start(); + + body.push(Statement::Store { + pointer: expr, + value: left, + }); + + self.emit_flush(body); + self.emit_start(); + + body.push(Statement::Store { pointer, value }); + + load + } else { + self.emit_flush(body); + self.emit_start(); + + body.push(Statement::Store { pointer, value }); + + left + } + } + _ => { + return Err(ErrorKind::SemanticError( + meta, + format!("{:?} cannot be in the left hand side", self.hir_exprs[expr]).into(), + )) + } + }; + + Ok((Some(handle), meta)) + } + + pub fn expr_scalar_components( + &mut self, + parser: &mut Parser, + expr: Handle, + meta: SourceMetadata, + ) -> Result, ErrorKind> { + let ty = parser.resolve_type(self, expr, meta)?; + Ok(scalar_components(ty)) + } + + pub fn expr_power( + &mut self, + parser: &mut Parser, + expr: Handle, + meta: SourceMetadata, + ) -> Result, ErrorKind> { + Ok(self + .expr_scalar_components(parser, expr, meta)? + .and_then(|(kind, _)| type_power(kind))) + } + + pub fn get_expression(&self, expr: Handle) -> &Expression { + &self.expressions[expr] + } + + pub fn implicit_conversion( + &mut self, + parser: &mut Parser, + expr: &mut Handle, + meta: SourceMetadata, + kind: ScalarKind, + width: crate::Bytes, + ) -> Result<(), ErrorKind> { + if let (Some(tgt_power), Some(expr_power)) = + (type_power(kind), self.expr_power(parser, *expr, meta)?) + { + if tgt_power > expr_power { + *expr = self.expressions.append(Expression::As { + expr: *expr, + kind, + convert: Some(width), + }) + } + } + + Ok(()) + } + + pub fn binary_implicit_conversion( + &mut self, + parser: &mut Parser, + left: &mut Handle, + left_meta: SourceMetadata, + right: &mut Handle, + right_meta: SourceMetadata, + ) -> Result<(), ErrorKind> { + let left_components = self.expr_scalar_components(parser, *left, left_meta)?; + let right_components = self.expr_scalar_components(parser, *right, right_meta)?; + + if let ( + Some((left_power, left_width, left_kind)), + Some((right_power, right_width, right_kind)), + ) = ( + left_components.and_then(|(kind, width)| Some((type_power(kind)?, width, kind))), + right_components.and_then(|(kind, width)| Some((type_power(kind)?, width, kind))), + ) { + match left_power.cmp(&right_power) { + std::cmp::Ordering::Less => { + *left = self.expressions.append(Expression::As { + expr: *left, + kind: right_kind, + convert: Some(right_width), + }) + } + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Greater => { + *right = self.expressions.append(Expression::As { + expr: *right, + kind: left_kind, + convert: Some(left_width), + }) + } + } + } + + Ok(()) + } + + pub fn implicit_splat( + &mut self, + parser: &mut Parser, + + expr: &mut Handle, + meta: SourceMetadata, + vector_size: Option, + ) -> Result<(), ErrorKind> { + let expr_type = parser.resolve_type(self, *expr, meta)?; + + if let (&TypeInner::Scalar { .. }, Some(size)) = (expr_type, vector_size) { + *expr = self + .expressions + .append(Expression::Splat { size, value: *expr }) + } + + Ok(()) + } + + pub fn vector_resize( + &mut self, + size: VectorSize, + vector: Handle, + body: &mut Block, + ) -> Handle { + self.add_expression( + Expression::Swizzle { + size, + vector, + pattern: crate::SwizzleComponent::XYZW, + }, + body, + ) + } +} + +impl Index> for Context { + type Output = Expression; + + fn index(&self, index: Handle) -> &Self::Output { + &self.expressions[index] + } +} diff --git a/src/front/glsl/functions.rs b/src/front/glsl/functions.rs index c5309d6b4f..3d3ab3932c 100644 --- a/src/front/glsl/functions.rs +++ b/src/front/glsl/functions.rs @@ -1,22 +1,29 @@ -use super::{ast::*, error::ErrorKind, SourceMetadata}; use crate::{ - proc::ensure_block_returns, Arena, BinaryOperator, Block, Constant, ConstantInner, EntryPoint, - Expression, Function, FunctionArgument, FunctionResult, Handle, ImageClass, ImageDimension, - ImageQuery, LocalVariable, MathFunction, Module, RelationalFunction, SampleLevel, ScalarKind, - ScalarValue, Statement, StructMember, Type, TypeInner, VectorSize, + front::glsl::{ + ast::*, + context::Context, + error::ErrorKind, + types::{scalar_components, type_power}, + Parser, SourceMetadata, + }, + proc::ensure_block_returns, + Arena, BinaryOperator, Block, Constant, ConstantInner, EntryPoint, Expression, FastHashMap, + Function, FunctionArgument, FunctionResult, Handle, ImageClass, ImageDimension, ImageQuery, + LocalVariable, MathFunction, Module, RelationalFunction, SampleLevel, ScalarKind, ScalarValue, + Statement, StructMember, Type, TypeInner, VectorSize, }; use std::iter; /// Helper struct for texture calls with the separate components from the vector argument /// -/// Obtained by calling [`coordinate_components`](Program::coordinate_components) +/// Obtained by calling [`coordinate_components`](Parser::coordinate_components) struct CoordComponents { coordinate: Handle, depth_ref: Option>, array_index: Option>, } -impl Program { +impl Parser { fn add_constant_value(&mut self, scalar_kind: ScalarKind, value: u64) -> Handle { let value = match scalar_kind { ScalarKind::Uint => ScalarValue::Uint(value), @@ -1056,33 +1063,40 @@ impl Program { pub fn add_function( &mut self, - mut function: Function, + ctx: Context, name: String, - // Normalized function parameters, modifiers are not applied - parameters: Vec>, - parameters_info: Vec, + result: Option, + mut body: Block, meta: SourceMetadata, ) -> Result<(), ErrorKind> { - ensure_block_returns(&mut function.body); + ensure_block_returns(&mut body); - if name.as_str() == "main" { - let handle = self.module.functions.append(function); - return if self.entry_point.replace(handle).is_some() { - Err(ErrorKind::SemanticError(meta, "main defined twice".into())) - } else { - Ok(()) - }; - } + let void = result.is_none(); - let void = function.result.is_none(); - - let &mut Program { + let &mut Parser { ref mut lookup_function, ref mut module, .. } = self; - let declarations = lookup_function.entry(name).or_default(); + let declarations = lookup_function.entry(name.clone()).or_default(); + let Context { + expressions, + locals, + arguments, + parameters, + parameters_info, + .. + } = ctx; + let function = Function { + name: Some(name), + arguments, + result, + local_variables: locals, + expressions, + named_expressions: FastHashMap::default(), + body, + }; 'outer: for decl in declarations.iter_mut() { if parameters.len() != decl.parameters.len() { @@ -1125,22 +1139,33 @@ impl Program { pub fn add_prototype( &mut self, - function: Function, + ctx: Context, name: String, - // Normalized function parameters, modifiers are not applied - parameters: Vec>, - parameters_info: Vec, + result: Option, meta: SourceMetadata, ) -> Result<(), ErrorKind> { - let void = function.result.is_none(); + let void = result.is_none(); - let &mut Program { + let &mut Parser { ref mut lookup_function, ref mut module, .. } = self; - let declarations = lookup_function.entry(name).or_default(); + let declarations = lookup_function.entry(name.clone()).or_default(); + + let Context { + arguments, + parameters, + parameters_info, + .. + } = ctx; + let function = Function { + name: Some(name), + arguments, + result, + ..Default::default() + }; 'outer: for decl in declarations.iter_mut() { if parameters.len() != decl.parameters.len() { @@ -1268,10 +1293,10 @@ impl Program { self.module.entry_points.push(EntryPoint { name: "main".to_string(), - stage: self.stage, + stage: self.meta.stage, early_depth_test: Some(crate::EarlyDepthTest { conservative: None }) - .filter(|_| self.early_fragment_tests), - workgroup_size: self.workgroup_size, + .filter(|_| self.meta.early_fragment_tests), + workgroup_size: self.meta.workgroup_size, function: Function { arguments, expressions, @@ -1286,7 +1311,7 @@ impl Program { /// Helper function for texture calls, splits the vector argument into it's components fn coordinate_components( - &self, + &mut self, ctx: &mut Context, (image, image_meta): (Handle, SourceMetadata), (coord, coord_meta): (Handle, SourceMetadata), diff --git a/src/front/glsl/mod.rs b/src/front/glsl/mod.rs index 88966d0346..8e487b8159 100644 --- a/src/front/glsl/mod.rs +++ b/src/front/glsl/mod.rs @@ -1,17 +1,17 @@ -pub use error::ErrorKind; +pub use ast::Profile; +pub use error::{ErrorKind, ParseError}; pub use token::{SourceMetadata, Token}; -use crate::{FastHashMap, Module, ShaderStage}; - -mod lex; +use crate::{FastHashMap, Handle, Module, ShaderStage, Type}; +use ast::{EntryArg, FunctionDeclaration, GlobalLookup}; +use parser::ParsingContext; mod ast; -use ast::Program; - -mod error; -pub use error::ParseError; mod constants; +mod context; +mod error; mod functions; +mod lex; mod offset; mod parser; #[cfg(test)] @@ -20,17 +20,95 @@ mod token; mod types; mod variables; +type Result = std::result::Result; + pub struct Options { pub stage: ShaderStage, pub defines: FastHashMap, } -pub fn parse_str(source: &str, options: &Options) -> Result { - let mut program = Program::new(options.stage); +#[derive(Debug)] +pub struct ShaderMetadata { + pub version: u16, + pub profile: Profile, + pub stage: ShaderStage, - let lex = lex::Lexer::new(source, &options.defines); - let mut parser = parser::Parser::new(&mut program, lex); - parser.parse()?; + pub workgroup_size: [u32; 3], + pub early_fragment_tests: bool, - Ok(program.module) + pub extensions: FastHashMap, +} + +impl ShaderMetadata { + fn reset(&mut self, stage: ShaderStage) { + self.version = 0; + self.profile = Profile::Core; + self.stage = stage; + self.workgroup_size = [if stage == ShaderStage::Compute { 1 } else { 0 }; 3]; + self.early_fragment_tests = false; + self.extensions.clear(); + } +} + +impl Default for ShaderMetadata { + fn default() -> Self { + ShaderMetadata { + version: 0, + profile: Profile::Core, + stage: ShaderStage::Vertex, + workgroup_size: [0; 3], + early_fragment_tests: false, + extensions: FastHashMap::default(), + } + } +} + +#[derive(Debug, Default)] +pub struct Parser { + meta: ShaderMetadata, + + lookup_function: FastHashMap>, + lookup_type: FastHashMap>, + + global_variables: Vec<(String, GlobalLookup)>, + + entry_args: Vec, + + module: Module, +} + +impl Parser { + fn reset(&mut self, stage: ShaderStage) { + self.meta.reset(stage); + + self.lookup_function.clear(); + self.lookup_type.clear(); + self.global_variables.clear(); + self.entry_args.clear(); + + // This is necessary because if the last parsing errored out, the module + // wouldn't have been swapped + self.module = Module::default(); + } + + pub fn parse( + &mut self, + options: &Options, + source: &str, + ) -> std::result::Result { + self.reset(options.stage); + + let lexer = lex::Lexer::new(source, &options.defines); + let mut ctx = ParsingContext::new(lexer); + + ctx.parse(self).map_err(|kind| ParseError { kind })?; + + let mut module = Module::default(); + std::mem::swap(&mut self.module, &mut module); + Ok(module) + } + + pub fn metadata(&self) -> &ShaderMetadata { + &self.meta + } } diff --git a/src/front/glsl/parser.rs b/src/front/glsl/parser.rs index d9bf53f749..56982391c6 100644 --- a/src/front/glsl/parser.rs +++ b/src/front/glsl/parser.rs @@ -1,42 +1,38 @@ -use super::{ - ast::{ - self, Context, FunctionCall, FunctionCallKind, GlobalLookup, GlobalLookupKind, HirExpr, - HirExprKind, ParameterQualifier, Profile, StorageQualifier, StructLayout, TypeQualifier, - }, - error::ErrorKind, - lex::Lexer, - offset, - token::{SourceMetadata, Token, TokenValue}, - variables::{GlobalOrConstant, VarDeclaration}, - Program, -}; use crate::{ arena::Handle, - front::glsl::{ast::Precision, error::ExpectedToken}, - Arena, ArraySize, BinaryOperator, Block, Constant, ConstantInner, Expression, Function, - FunctionResult, ResourceBinding, ScalarKind, ScalarValue, Statement, StorageClass, - StructMember, SwitchCase, Type, TypeInner, UnaryOperator, + front::glsl::{ + ast::{Profile, TypeQualifier}, + context::Context, + error::ErrorKind, + error::ExpectedToken, + lex::Lexer, + token::{SourceMetadata, Token, TokenValue}, + variables::{GlobalOrConstant, VarDeclaration}, + Parser, Result, + }, + Block, Constant, ConstantInner, Expression, ScalarValue, Type, }; use core::convert::TryFrom; use std::iter::Peekable; -type Result = std::result::Result; +mod declarations; +mod expressions; +mod functions; +mod types; -pub struct Parser<'source, 'program> { - program: &'program mut Program, +pub struct ParsingContext<'source> { lexer: Peekable>, } -impl<'source, 'program> Parser<'source, 'program> { - pub fn new(program: &'program mut Program, lexer: Lexer<'source>) -> Self { - Parser { - program, +impl<'source> ParsingContext<'source> { + pub fn new(lexer: Lexer<'source>) -> Self { + ParsingContext { lexer: lexer.peekable(), } } - fn expect_ident(&mut self) -> Result<(String, SourceMetadata)> { - let token = self.bump()?; + pub fn expect_ident(&mut self, parser: &mut Parser) -> Result<(String, SourceMetadata)> { + let token = self.bump(parser)?; match token.value { TokenValue::Identifier(name) => Ok((name, token.meta)), @@ -47,8 +43,8 @@ impl<'source, 'program> Parser<'source, 'program> { } } - fn expect(&mut self, value: TokenValue) -> Result { - let token = self.bump()?; + pub fn expect(&mut self, parser: &mut Parser, value: TokenValue) -> Result { + let token = self.bump(parser)?; if token.value != value { Err(ErrorKind::InvalidToken(token, vec![value.into()])) @@ -57,57 +53,67 @@ impl<'source, 'program> Parser<'source, 'program> { } } - fn bump(&mut self) -> Result { - self.lexer.next().ok_or(ErrorKind::EndOfFile) + pub fn next(&mut self, _parser: &mut Parser) -> Option { + self.lexer.next() + } + + pub fn bump(&mut self, parser: &mut Parser) -> Result { + self.next(parser).ok_or(ErrorKind::EndOfFile) } /// Returns None on the end of the file rather than an error like other methods - fn bump_if(&mut self, value: TokenValue) -> Option { - if self.lexer.peek().filter(|t| t.value == value).is_some() { - self.bump().ok() + pub fn bump_if(&mut self, parser: &mut Parser, value: TokenValue) -> Option { + if self.peek(parser).filter(|t| t.value == value).is_some() { + self.bump(parser).ok() } else { None } } - fn expect_peek(&mut self) -> Result<&Token> { - self.lexer.peek().ok_or(ErrorKind::EndOfFile) + pub fn peek(&mut self, _parser: &mut Parser) -> Option<&Token> { + self.lexer.peek() } - pub fn parse(&mut self) -> Result<()> { - self.parse_version()?; + pub fn expect_peek(&mut self, parser: &mut Parser) -> Result<&Token> { + self.peek(parser).ok_or(ErrorKind::EndOfFile) + } + + pub fn parse(&mut self, parser: &mut Parser) -> Result<()> { + self.parse_version(parser)?; // Body and expression arena for global initialization let mut body = Block::new(); - let mut expressions = Arena::new(); - let mut arguments = Vec::new(); - let mut locals = Arena::new(); - let mut ctx = Context::new( - self.program, - &mut body, - &mut expressions, - &mut locals, - &mut arguments, - ); + let mut ctx = Context::new(parser, &mut body); - while self.lexer.peek().is_some() { - self.parse_external_declaration(&mut ctx, &mut body)?; + while self.peek(parser).is_some() { + self.parse_external_declaration(parser, &mut ctx, &mut body)?; } - if let Some(handle) = self.program.entry_point { - self.program.add_entry_point(handle, body, expressions)?; - } + let handle = parser + .lookup_function + .get("main") + .and_then(|declarations| { + declarations + .iter() + .find(|decl| decl.defined && decl.parameters.is_empty()) + .map(|decl| decl.handle) + }) + .ok_or_else(|| { + ErrorKind::SemanticError(SourceMetadata::default(), "Missing entry point".into()) + })?; + + parser.add_entry_point(handle, body, ctx.expressions)?; Ok(()) } - fn parse_version(&mut self) -> Result<()> { - self.expect(TokenValue::Version)?; + fn parse_version(&mut self, parser: &mut Parser) -> Result<()> { + self.expect(parser, TokenValue::Version)?; - let version = self.bump()?; + let version = self.bump(parser)?; match version.value { TokenValue::IntConstant(i) => match i.value { - 440 | 450 | 460 => self.program.version = i.value as u16, + 440 | 450 | 460 => parser.meta.version = i.value as u16, _ => return Err(ErrorKind::InvalidVersion(version.meta, i.value)), }, _ => { @@ -119,12 +125,12 @@ impl<'source, 'program> Parser<'source, 'program> { } let profile = self.lexer.peek(); - self.program.profile = match profile { + parser.meta.profile = match profile { Some(&Token { value: TokenValue::Identifier(_), .. }) => { - let (name, meta) = self.expect_ident()?; + let (name, meta) = self.expect_ident(parser)?; match name.as_str() { "core" => Profile::Core, @@ -137,194 +143,10 @@ impl<'source, 'program> Parser<'source, 'program> { Ok(()) } - /// Parses an optional array_specifier returning `Ok(None)` if there is no - /// LeftBracket - fn parse_array_specifier(&mut self) -> Result> { - if self.bump_if(TokenValue::LeftBracket).is_some() { - if self.bump_if(TokenValue::RightBracket).is_some() { - return Ok(Some(ArraySize::Dynamic)); - } + fn parse_uint_constant(&mut self, parser: &mut Parser) -> Result<(u32, SourceMetadata)> { + let (value, meta) = self.parse_constant_expression(parser)?; - let (constant, _) = self.parse_constant_expression()?; - self.expect(TokenValue::RightBracket)?; - Ok(Some(ArraySize::Constant(constant))) - } else { - Ok(None) - } - } - - fn parse_type(&mut self) -> Result<(Option>, SourceMetadata)> { - let token = self.bump()?; - let handle = match token.value { - TokenValue::Void => None, - TokenValue::TypeName(ty) => Some(self.program.module.types.fetch_or_append(ty)), - TokenValue::Struct => { - let ty_name = self.expect_ident()?.0; - self.expect(TokenValue::LeftBrace)?; - let mut members = Vec::new(); - let span = - self.parse_struct_declaration_list(&mut members, StructLayout::Std140)?; - self.expect(TokenValue::RightBrace)?; - - let ty = self.program.module.types.append(Type { - name: Some(ty_name.clone()), - inner: TypeInner::Struct { - top_level: false, - members, - span, - }, - }); - self.program.lookup_type.insert(ty_name, ty); - Some(ty) - } - TokenValue::Identifier(ident) => match self.program.lookup_type.get(&ident) { - Some(ty) => Some(*ty), - None => return Err(ErrorKind::UnknownType(token.meta, ident)), - }, - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ - TokenValue::Void.into(), - TokenValue::Struct.into(), - ExpectedToken::TypeName, - ], - )) - } - }; - - let size = self.parse_array_specifier()?; - Ok((handle.map(|ty| self.maybe_array(ty, size)), token.meta)) - } - - fn parse_type_non_void(&mut self) -> Result<(Handle, SourceMetadata)> { - let (maybe_ty, meta) = self.parse_type()?; - let ty = - maybe_ty.ok_or_else(|| ErrorKind::SemanticError(meta, "Type can't be void".into()))?; - - Ok((ty, meta)) - } - - fn maybe_array(&mut self, base: Handle, size: Option) -> Handle { - size.map(|size| { - self.program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Array { - base, - size, - stride: self.program.module.types[base] - .inner - .span(&self.program.module.constants), - }, - }) - }) - .unwrap_or(base) - } - - fn peek_type_qualifier(&mut self) -> bool { - self.lexer.peek().map_or(false, |t| match t.value { - TokenValue::Interpolation(_) - | TokenValue::Sampling(_) - | TokenValue::PrecisionQualifier(_) - | TokenValue::Const - | TokenValue::In - | TokenValue::Out - | TokenValue::Uniform - | TokenValue::Buffer - | TokenValue::Restrict - | TokenValue::StorageAccess(_) - | TokenValue::Layout => true, - _ => false, - }) - } - - fn parse_type_qualifiers(&mut self) -> Result> { - let mut qualifiers = Vec::new(); - - while self.peek_type_qualifier() { - let token = self.bump()?; - - // Handle layout qualifiers outside the match since this can push multiple values - if token.value == TokenValue::Layout { - self.parse_layout_qualifier_id_list(&mut qualifiers)?; - continue; - } - - qualifiers.push(( - match token.value { - TokenValue::Interpolation(i) => TypeQualifier::Interpolation(i), - TokenValue::Const => TypeQualifier::StorageQualifier(StorageQualifier::Const), - TokenValue::In => TypeQualifier::StorageQualifier(StorageQualifier::Input), - TokenValue::Out => TypeQualifier::StorageQualifier(StorageQualifier::Output), - TokenValue::Uniform => TypeQualifier::StorageQualifier( - StorageQualifier::StorageClass(StorageClass::Uniform), - ), - TokenValue::Buffer => TypeQualifier::StorageQualifier( - StorageQualifier::StorageClass(StorageClass::Storage { - access: crate::StorageAccess::default(), - }), - ), - TokenValue::Sampling(s) => TypeQualifier::Sampling(s), - TokenValue::PrecisionQualifier(p) => TypeQualifier::Precision(p), - TokenValue::StorageAccess(access) => TypeQualifier::StorageAccess(access), - TokenValue::Restrict => continue, - _ => unreachable!(), - }, - token.meta, - )) - } - - Ok(qualifiers) - } - - fn parse_layout_qualifier_id_list( - &mut self, - qualifiers: &mut Vec<(TypeQualifier, SourceMetadata)>, - ) -> Result<()> { - // We need both of these to produce a ResourceBinding - let mut group = None; - let mut binding = None; - - self.expect(TokenValue::LeftParen)?; - loop { - self.parse_layout_qualifier_id(qualifiers, &mut group, &mut binding)?; - - if self.bump_if(TokenValue::Comma).is_some() { - continue; - } - - break; - } - self.expect(TokenValue::RightParen)?; - - match (group, binding) { - (Some((group, group_meta)), Some((binding, binding_meta))) => qualifiers.push(( - TypeQualifier::ResourceBinding(ResourceBinding { group, binding }), - group_meta.union(&binding_meta), - )), - // Produce an error if we have one of group or binding but not the other - (Some((_, meta)), None) => { - return Err(ErrorKind::SemanticError( - meta, - "set specified with no binding".into(), - )) - } - (None, Some((_, meta))) => { - return Err(ErrorKind::SemanticError( - meta, - "binding specified with no set".into(), - )) - } - (None, None) => (), - } - - Ok(()) - } - - fn parse_uint_constant(&mut self) -> Result<(u32, SourceMetadata)> { - let (value, meta) = self.parse_constant_expression()?; - - let int = match self.program.module.constants[value].inner { + let int = match parser.module.constants[value].inner { ConstantInner::Scalar { value: ScalarValue::Uint(int), .. @@ -344,1628 +166,33 @@ impl<'source, 'program> Parser<'source, 'program> { Ok((int, meta)) } - fn parse_layout_qualifier_id( + fn parse_constant_expression( &mut self, - qualifiers: &mut Vec<(TypeQualifier, SourceMetadata)>, - group: &mut Option<(u32, SourceMetadata)>, - binding: &mut Option<(u32, SourceMetadata)>, - ) -> Result<()> { - // layout_qualifier_id: - // IDENTIFIER - // IDENTIFIER EQUAL constant_expression - // SHARED - let mut token = self.bump()?; - match token.value { - TokenValue::Identifier(name) => { - if self.bump_if(TokenValue::Assign).is_some() { - let (value, end_meta) = self.parse_uint_constant()?; - token.meta = token.meta.union(&end_meta); - - qualifiers.push(( - match name.as_str() { - "location" => TypeQualifier::Location(value), - "set" => { - *group = Some((value, end_meta)); - return Ok(()); - } - "binding" => { - *binding = Some((value, end_meta)); - return Ok(()); - } - "local_size_x" => TypeQualifier::WorkGroupSize(0, value), - "local_size_y" => TypeQualifier::WorkGroupSize(1, value), - "local_size_z" => TypeQualifier::WorkGroupSize(2, value), - _ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)), - }, - token.meta, - )) - } else { - match name.as_str() { - "push_constant" => { - qualifiers.push(( - TypeQualifier::StorageQualifier(StorageQualifier::StorageClass( - StorageClass::PushConstant, - )), - token.meta, - )); - qualifiers - .push((TypeQualifier::Layout(StructLayout::Std430), token.meta)); - } - "std140" => qualifiers - .push((TypeQualifier::Layout(StructLayout::Std140), token.meta)), - "std430" => qualifiers - .push((TypeQualifier::Layout(StructLayout::Std430), token.meta)), - "early_fragment_tests" => { - qualifiers.push((TypeQualifier::EarlyFragmentTests, token.meta)) - } - _ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)), - } - }; - - Ok(()) - } - // TODO: handle Shared? - _ => Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier], - )), - } - } - - fn parse_constant_expression(&mut self) -> Result<(Handle, SourceMetadata)> { - let mut expressions = Arena::new(); - let mut locals = Arena::new(); - let mut arguments = Vec::new(); + parser: &mut Parser, + ) -> Result<(Handle, SourceMetadata)> { let mut block = Block::new(); - let mut ctx = Context::new( - self.program, - &mut block, - &mut expressions, - &mut locals, - &mut arguments, - ); + let mut ctx = Context::new(parser, &mut block); - let expr = self.parse_conditional(&mut ctx, &mut block, None)?; - let (root, meta) = ctx.lower_expect(self.program, expr, false, &mut block)?; + let expr = self.parse_conditional(parser, &mut ctx, &mut block, None)?; + let (root, meta) = ctx.lower_expect(parser, expr, false, &mut block)?; - Ok((self.program.solve_constant(&ctx, root, meta)?, meta)) - } - - fn parse_external_declaration( - &mut self, - global_ctx: &mut Context, - global_body: &mut Block, - ) -> Result<()> { - if !self.parse_declaration(global_ctx, global_body, true)? { - let token = self.bump()?; - match token.value { - TokenValue::Semicolon if self.program.version == 460 => Ok(()), - _ => { - let expected = match self.program.version { - 460 => vec![TokenValue::Semicolon.into(), ExpectedToken::Eof], - _ => vec![ExpectedToken::Eof], - }; - Err(ErrorKind::InvalidToken(token, expected)) - } - } - } else { - Ok(()) - } - } - - fn peek_type_name(&mut self) -> bool { - let program = &self.program; - self.lexer.peek().map_or(false, |t| match t.value { - TokenValue::TypeName(_) | TokenValue::Void => true, - TokenValue::Struct => true, - TokenValue::Identifier(ref ident) => program.lookup_type.contains_key(ident), - _ => false, - }) - } - - fn peek_parameter_qualifier(&mut self) -> bool { - self.lexer.peek().map_or(false, |t| match t.value { - TokenValue::In | TokenValue::Out | TokenValue::InOut | TokenValue::Const => true, - _ => false, - }) - } - - /// Returns the parsed `ParameterQualifier` or `ParameterQualifier::In` - fn parse_parameter_qualifier(&mut self) -> ParameterQualifier { - if self.peek_parameter_qualifier() { - match self.bump().unwrap().value { - TokenValue::In => ParameterQualifier::In, - TokenValue::Out => ParameterQualifier::Out, - TokenValue::InOut => ParameterQualifier::InOut, - TokenValue::Const => ParameterQualifier::Const, - _ => unreachable!(), - } - } else { - ParameterQualifier::In - } - } - - fn parse_initializer( - &mut self, - ty: Handle, - ctx: &mut Context, - body: &mut Block, - ) -> Result<(Handle, SourceMetadata)> { - // initializer: - // assignment_expression - // LEFT_BRACE initializer_list RIGHT_BRACE - // LEFT_BRACE initializer_list COMMA RIGHT_BRACE - // - // initializer_list: - // initializer - // initializer_list COMMA initializer - if let Some(Token { mut meta, .. }) = self.bump_if(TokenValue::LeftBrace) { - // initializer_list - let mut components = Vec::new(); - loop { - // TODO: Change type - components.push(self.parse_initializer(ty, ctx, body)?.0); - - let token = self.bump()?; - match token.value { - TokenValue::Comma => { - if let Some(Token { meta: end_meta, .. }) = - self.bump_if(TokenValue::RightBrace) - { - meta = meta.union(&end_meta); - break; - } - } - TokenValue::RightBrace => { - meta = meta.union(&token.meta); - break; - } - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![TokenValue::Comma.into(), TokenValue::RightBrace.into()], - )) - } - } - } - - Ok(( - ctx.add_expression(Expression::Compose { ty, components }, body), - meta, - )) - } else { - let expr = self.parse_assignment(ctx, body)?; - let (mut init, init_meta) = ctx.lower_expect(self.program, expr, false, body)?; - - let scalar_components = ast::scalar_components(&self.program.module.types[ty].inner); - if let Some((kind, width)) = scalar_components { - ctx.implicit_conversion(self.program, &mut init, init_meta, kind, width)?; - } - - Ok((init, init_meta)) - } - } - - // Note: caller preparsed the type and qualifiers - // Note: caller skips this if the fallthrough token is not expected to be consumed here so this - // produced Error::InvalidToken if it isn't consumed - fn parse_init_declarator_list( - &mut self, - ty: Handle, - mut fallthrough: Option, - ctx: &mut DeclarationContext, - ) -> Result<()> { - // init_declarator_list: - // single_declaration - // init_declarator_list COMMA IDENTIFIER - // init_declarator_list COMMA IDENTIFIER array_specifier - // init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer - // init_declarator_list COMMA IDENTIFIER EQUAL initializer - // - // single_declaration: - // fully_specified_type - // fully_specified_type IDENTIFIER - // fully_specified_type IDENTIFIER array_specifier - // fully_specified_type IDENTIFIER array_specifier EQUAL initializer - // fully_specified_type IDENTIFIER EQUAL initializer - - // Consume any leading comma, e.g. this is valid: `float, a=1;` - if fallthrough - .as_ref() - .or_else(|| self.lexer.peek()) - .filter(|t| t.value == TokenValue::Comma) - .is_some() - { - fallthrough.take().or_else(|| self.lexer.next()); - } - - loop { - let token = fallthrough - .take() - .ok_or(ErrorKind::EndOfFile) - .or_else(|_| self.bump())?; - let name = match token.value { - TokenValue::Semicolon => break, - TokenValue::Identifier(name) => name, - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], - )) - } - }; - let mut meta = token.meta; - - // array_specifier - // array_specifier EQUAL initializer - // EQUAL initializer - - // parse an array specifier if it exists - // NOTE: unlike other parse methods this one doesn't expect an array specifier and - // returns Ok(None) rather than an error if there is not one - let array_specifier = self.parse_array_specifier()?; - let ty = self.maybe_array(ty, array_specifier); - - let init = self - .bump_if(TokenValue::Assign) - .map::, _>(|_| { - let (mut expr, init_meta) = self.parse_initializer(ty, ctx.ctx, ctx.body)?; - - let scalar_components = - ast::scalar_components(&self.program.module.types[ty].inner); - if let Some((kind, width)) = scalar_components { - ctx.ctx.implicit_conversion( - self.program, - &mut expr, - init_meta, - kind, - width, - )?; - } - - meta = meta.union(&init_meta); - - Ok((expr, init_meta)) - }) - .transpose()?; - - // TODO: Should we try to make constants here? - // This is mostly a hack because we don't yet support adding - // bodies to entry points for variable initialization - let maybe_constant = - init.and_then(|(root, meta)| self.program.solve_constant(ctx.ctx, root, meta).ok()); - - let pointer = ctx.add_var(self.program, ty, name, maybe_constant, meta)?; - - if let Some((value, _)) = init.filter(|_| maybe_constant.is_none()) { - ctx.flush_expressions(); - ctx.body.push(Statement::Store { pointer, value }); - } - - let token = self.bump()?; - match token.value { - TokenValue::Semicolon => break, - TokenValue::Comma => {} - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![TokenValue::Comma.into(), TokenValue::Semicolon.into()], - )) - } - } - } - - Ok(()) - } - - /// `external` whether or not we are in a global or local context - fn parse_declaration( - &mut self, - ctx: &mut Context, - body: &mut Block, - external: bool, - ) -> Result { - //declaration: - // function_prototype SEMICOLON - // - // init_declarator_list SEMICOLON - // PRECISION precision_qualifier type_specifier SEMICOLON - // - // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE SEMICOLON - // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON - // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER array_specifier SEMICOLON - // type_qualifier SEMICOLON type_qualifier IDENTIFIER SEMICOLON - // type_qualifier IDENTIFIER identifier_list SEMICOLON - - if self.peek_type_qualifier() || self.peek_type_name() { - let qualifiers = self.parse_type_qualifiers()?; - - if self.peek_type_name() { - // This branch handles variables and function prototypes and if - // external is true also function definitions - let (ty, mut meta) = self.parse_type()?; - - let token = self.bump()?; - let token_fallthrough = match token.value { - TokenValue::Identifier(name) => match self.expect_peek()?.value { - TokenValue::LeftParen => { - // This branch handles function definition and prototypes - self.bump()?; - - let result = ty.map(|ty| FunctionResult { ty, binding: None }); - let mut expressions = Arena::new(); - let mut local_variables = Arena::new(); - let mut arguments = Vec::new(); - // Normalized function parameters, modifiers are not applied - let mut parameters = Vec::new(); - let mut body = Block::new(); - - let mut context = Context::new( - self.program, - &mut body, - &mut expressions, - &mut local_variables, - &mut arguments, - ); - - self.parse_function_args(&mut context, &mut body, &mut parameters)?; - - let end_meta = self.expect(TokenValue::RightParen)?.meta; - meta = meta.union(&end_meta); - - let token = self.bump()?; - return match token.value { - TokenValue::Semicolon => { - // This branch handles function prototypes - - let Context { - parameters_info, .. - } = context; - - self.program.add_prototype( - Function { - name: Some(name.clone()), - result, - arguments, - ..Default::default() - }, - name, - parameters, - parameters_info, - meta, - )?; - - Ok(true) - } - TokenValue::LeftBrace if external => { - // This branch handles function definitions - // as you can see by the guard this branch - // only happens if external is also true - - // parse the body - self.parse_compound_statement(&mut context, &mut body)?; - - let Context { - parameters_info, .. - } = context; - self.program.add_function( - Function { - name: Some(name.clone()), - result, - expressions, - named_expressions: crate::FastHashMap::default(), - local_variables, - arguments, - body, - }, - name, - parameters, - parameters_info, - meta, - )?; - - Ok(true) - } - _ if external => Err(ErrorKind::InvalidToken( - token, - vec![ - TokenValue::LeftBrace.into(), - TokenValue::Semicolon.into(), - ], - )), - _ => Err(ErrorKind::InvalidToken( - token, - vec![TokenValue::Semicolon.into()], - )), - }; - } - // Pass the token to the init_declator_list parser - _ => Token { - value: TokenValue::Identifier(name), - meta: token.meta, - }, - }, - // Pass the token to the init_declator_list parser - _ => token, - }; - - // If program execution has reached here then this will be a - // init_declarator_list - // token_falltrough will have a token that was already bumped - if let Some(ty) = ty { - let mut ctx = DeclarationContext { - qualifiers, - external, - ctx, - body, - }; - - self.parse_init_declarator_list(ty, Some(token_fallthrough), &mut ctx)?; - } else { - return Err(ErrorKind::SemanticError( - meta, - "Declaration cannot have void type".into(), - )); - } - - Ok(true) - } else { - // This branch handles struct definitions and modifiers like - // ```glsl - // layout(early_fragment_tests); - // ``` - let token = self.bump()?; - match token.value { - TokenValue::Identifier(ty_name) => { - if self.bump_if(TokenValue::LeftBrace).is_some() { - self.parse_block_declaration( - ctx, - body, - &qualifiers, - ty_name, - token.meta, - ) - } else { - //TODO: declaration - // type_qualifier IDENTIFIER SEMICOLON - // type_qualifier IDENTIFIER identifier_list SEMICOLON - Err(ErrorKind::NotImplemented(token.meta, "variable qualifier")) - } - } - TokenValue::Semicolon => { - for &(ref qualifier, meta) in qualifiers.iter() { - match *qualifier { - TypeQualifier::WorkGroupSize(i, value) => { - self.program.workgroup_size[i] = value - } - TypeQualifier::EarlyFragmentTests => { - self.program.early_fragment_tests = true; - } - TypeQualifier::StorageQualifier(_) => { - // TODO: Maybe add some checks here - // This is needed because of cases like - // layout(early_fragment_tests) in; - } - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Qualifier not supported as standalone".into(), - )); - } - } - } - - Ok(true) - } - _ => Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], - )), - } - } - } else { - match self.lexer.peek().map(|t| &t.value) { - Some(&TokenValue::Precision) => { - // PRECISION precision_qualifier type_specifier SEMICOLON - self.bump()?; - - let token = self.bump()?; - let _ = match token.value { - TokenValue::PrecisionQualifier(p) => p, - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ - TokenValue::PrecisionQualifier(Precision::High).into(), - TokenValue::PrecisionQualifier(Precision::Medium).into(), - TokenValue::PrecisionQualifier(Precision::Low).into(), - ], - )) - } - }; - - let (ty, meta) = self.parse_type_non_void()?; - - match self.program.module.types[ty].inner { - TypeInner::Scalar { - kind: ScalarKind::Float, - .. - } - | TypeInner::Scalar { - kind: ScalarKind::Sint, - .. - } => {} - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Precision statement can only work on floats and ints".into(), - )) - } - } - - self.expect(TokenValue::Semicolon)?; - - Ok(true) - } - _ => Ok(false), - } - } - } - - fn parse_block_declaration( - &mut self, - ctx: &mut Context, - body: &mut Block, - qualifiers: &[(TypeQualifier, SourceMetadata)], - ty_name: String, - mut meta: SourceMetadata, - ) -> Result { - let mut storage = None; - let mut layout = None; - - for &(ref qualifier, _) in qualifiers { - match *qualifier { - TypeQualifier::StorageQualifier(StorageQualifier::StorageClass(c)) => { - storage = Some(c) - } - TypeQualifier::Layout(l) => layout = Some(l), - _ => continue, - } - } - - let layout = match (layout, storage) { - (Some(layout), _) => layout, - (None, Some(StorageClass::Storage { .. })) => StructLayout::Std430, - _ => StructLayout::Std140, - }; - - let mut members = Vec::new(); - let span = self.parse_struct_declaration_list(&mut members, layout)?; - self.expect(TokenValue::RightBrace)?; - - let mut ty = self.program.module.types.append(Type { - name: Some(ty_name), - inner: TypeInner::Struct { - top_level: true, - members: members.clone(), - span, - }, - }); - - let token = self.bump()?; - let name = match token.value { - TokenValue::Semicolon => None, - TokenValue::Identifier(name) => { - if let Some(size) = self.parse_array_specifier()? { - ty = self.program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Array { - base: ty, - size, - stride: self.program.module.types[ty] - .inner - .span(&self.program.module.constants), - }, - }); - } - - self.expect(TokenValue::Semicolon)?; - - Some(name) - } - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], - )) - } - }; - meta = meta.union(&token.meta); - - let global = self.program.add_global_var( - ctx, - body, - VarDeclaration { - qualifiers, - ty, - name, - init: None, - meta, - }, - )?; - - for (i, k) in members - .into_iter() - .enumerate() - .filter_map(|(i, m)| m.name.map(|s| (i as u32, s))) - { - let lookup = GlobalLookup { - kind: match global { - GlobalOrConstant::Global(handle) => GlobalLookupKind::BlockSelect(handle, i), - GlobalOrConstant::Constant(handle) => GlobalLookupKind::Constant(handle), - }, - entry_arg: None, - mutable: true, - }; - ctx.add_global(&k, lookup, self.program, body); - - self.program.global_variables.push((k, lookup)); - } - - Ok(true) - } - - // TODO: Accept layout arguments - fn parse_struct_declaration_list( - &mut self, - members: &mut Vec, - layout: StructLayout, - ) -> Result { - let mut span = 0; - let mut align = 0; - - loop { - // TODO: type_qualifier - - let (ty, mut meta) = self.parse_type_non_void()?; - let (name, end_meta) = self.expect_ident()?; - - meta = meta.union(&end_meta); - - let array_specifier = self.parse_array_specifier()?; - let ty = self.maybe_array(ty, array_specifier); - - self.expect(TokenValue::Semicolon)?; - - let info = offset::calculate_offset( - ty, - meta, - layout, - &mut self.program.module.types, - &self.program.module.constants, - )?; - - span = offset::align_up(span, info.align); - align = align.max(info.align); - - members.push(StructMember { - name: Some(name), - ty: info.ty, - binding: None, - offset: span, - }); - - span += info.span; - - if let TokenValue::RightBrace = self.expect_peek()?.value { - break; - } - } - - span = offset::align_up(span, align); - - Ok(span) - } - - fn parse_primary(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - let mut token = self.bump()?; - - let (width, value) = match token.value { - TokenValue::IntConstant(int) => ( - (int.width / 8) as u8, - if int.signed { - ScalarValue::Sint(int.value as i64) - } else { - ScalarValue::Uint(int.value) - }, - ), - TokenValue::FloatConstant(float) => ( - (float.width / 8) as u8, - ScalarValue::Float(float.value as f64), - ), - TokenValue::BoolConstant(value) => (1, ScalarValue::Bool(value)), - TokenValue::LeftParen => { - let expr = self.parse_expression(ctx, body)?; - let meta = self.expect(TokenValue::RightParen)?.meta; - - token.meta = token.meta.union(&meta); - - return Ok(expr); - } - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![ - TokenValue::LeftParen.into(), - ExpectedToken::IntLiteral, - ExpectedToken::FloatLiteral, - ExpectedToken::BoolLiteral, - ], - )) - } - }; - - let handle = self.program.module.constants.fetch_or_append(Constant { - name: None, - specialization: None, - inner: ConstantInner::Scalar { width, value }, - }); - - Ok(ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Constant(handle), - meta: token.meta, - })) - } - - fn parse_function_call_args( - &mut self, - ctx: &mut Context, - body: &mut Block, - meta: &mut SourceMetadata, - ) -> Result>> { - let mut args = Vec::new(); - if let Some(token) = self.bump_if(TokenValue::RightParen) { - *meta = meta.union(&token.meta); - } else { - loop { - args.push(self.parse_assignment(ctx, body)?); - - let token = self.bump()?; - match token.value { - TokenValue::Comma => {} - TokenValue::RightParen => { - *meta = meta.union(&token.meta); - break; - } - _ => { - return Err(ErrorKind::InvalidToken( - token, - vec![TokenValue::Comma.into(), TokenValue::RightParen.into()], - )) - } - } - } - } - - Ok(args) - } - - fn parse_postfix(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - let mut base = match self.expect_peek()?.value { - TokenValue::Identifier(_) => { - let (name, mut meta) = self.expect_ident()?; - - let expr = if self.bump_if(TokenValue::LeftParen).is_some() { - let args = self.parse_function_call_args(ctx, body, &mut meta)?; - - let kind = match self.program.lookup_type.get(&name) { - Some(ty) => FunctionCallKind::TypeConstructor(*ty), - None => FunctionCallKind::Function(name), - }; - - HirExpr { - kind: HirExprKind::Call(FunctionCall { kind, args }), - meta, - } - } else { - let var = match self.program.lookup_variable(ctx, body, &name)? { - Some(var) => var, - None => return Err(ErrorKind::UnknownVariable(meta, name)), - }; - - HirExpr { - kind: HirExprKind::Variable(var), - meta, - } - }; - - ctx.hir_exprs.append(expr) - } - TokenValue::TypeName(_) => { - let Token { value, mut meta } = self.bump()?; - - let mut handle = if let TokenValue::TypeName(ty) = value { - self.program.module.types.fetch_or_append(ty) - } else { - unreachable!() - }; - - let maybe_size = self.parse_array_specifier()?; - - self.expect(TokenValue::LeftParen)?; - let args = self.parse_function_call_args(ctx, body, &mut meta)?; - - if let Some(array_size) = maybe_size { - let stride = self.program.module.types[handle] - .inner - .span(&self.program.module.constants); - - let size = match array_size { - ArraySize::Constant(size) => ArraySize::Constant(size), - ArraySize::Dynamic => { - let constant = - self.program.module.constants.fetch_or_append(Constant { - name: None, - specialization: None, - inner: ConstantInner::Scalar { - width: 4, - value: ScalarValue::Sint(args.len() as i64), - }, - }); - - ArraySize::Constant(constant) - } - }; - - handle = self.program.module.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Array { - base: handle, - size, - stride, - }, - }) - } - - ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Call(FunctionCall { - kind: FunctionCallKind::TypeConstructor(handle), - args, - }), - meta, - }) - } - _ => self.parse_primary(ctx, body)?, - }; - - while let TokenValue::LeftBracket - | TokenValue::Dot - | TokenValue::Increment - | TokenValue::Decrement = self.expect_peek()?.value - { - let Token { value, meta } = self.bump()?; - - match value { - TokenValue::LeftBracket => { - let index = self.parse_expression(ctx, body)?; - let end_meta = self.expect(TokenValue::RightBracket)?.meta; - - base = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Access { base, index }, - meta: meta.union(&end_meta), - }) - } - TokenValue::Dot => { - let (field, end_meta) = self.expect_ident()?; - - base = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Select { base, field }, - meta: meta.union(&end_meta), - }) - } - TokenValue::Increment => { - base = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::IncDec { - increment: true, - postfix: true, - expr: base, - }, - meta, - }) - } - TokenValue::Decrement => { - base = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::IncDec { - increment: false, - postfix: true, - expr: base, - }, - meta, - }) - } - _ => unreachable!(), - } - } - - Ok(base) - } - - fn parse_unary(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - // TODO: prefix inc/dec - Ok(match self.expect_peek()?.value { - TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => { - let Token { value, meta } = self.bump()?; - - let expr = self.parse_unary(ctx, body)?; - let end_meta = ctx.hir_exprs[expr].meta; - - let kind = match value { - TokenValue::Dash => HirExprKind::Unary { - op: UnaryOperator::Negate, - expr, - }, - TokenValue::Bang | TokenValue::Tilde => HirExprKind::Unary { - op: UnaryOperator::Not, - expr, - }, - _ => return Ok(expr), - }; - - ctx.hir_exprs.append(HirExpr { - kind, - meta: meta.union(&end_meta), - }) - } - TokenValue::Increment | TokenValue::Decrement => { - let Token { value, meta } = self.bump()?; - - let expr = self.parse_unary(ctx, body)?; - - ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::IncDec { - increment: match value { - TokenValue::Increment => true, - TokenValue::Decrement => false, - _ => unreachable!(), - }, - postfix: false, - expr, - }, - meta, - }) - } - _ => self.parse_postfix(ctx, body)?, - }) - } - - fn parse_binary( - &mut self, - ctx: &mut Context, - body: &mut Block, - passtrough: Option>, - min_bp: u8, - ) -> Result> { - let mut left = passtrough - .ok_or(ErrorKind::EndOfFile /* Dummy error */) - .or_else(|_| self.parse_unary(ctx, body))?; - let start_meta = ctx.hir_exprs[left].meta; - - while let Some((l_bp, r_bp)) = binding_power(&self.expect_peek()?.value) { - if l_bp < min_bp { - break; - } - - let Token { value, .. } = self.bump()?; - - let right = self.parse_binary(ctx, body, None, r_bp)?; - let end_meta = ctx.hir_exprs[right].meta; - - left = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Binary { - left, - op: match value { - TokenValue::LogicalOr => BinaryOperator::LogicalOr, - TokenValue::LogicalXor => BinaryOperator::NotEqual, - TokenValue::LogicalAnd => BinaryOperator::LogicalAnd, - TokenValue::VerticalBar => BinaryOperator::InclusiveOr, - TokenValue::Caret => BinaryOperator::ExclusiveOr, - TokenValue::Ampersand => BinaryOperator::And, - TokenValue::Equal => BinaryOperator::Equal, - TokenValue::NotEqual => BinaryOperator::NotEqual, - TokenValue::GreaterEqual => BinaryOperator::GreaterEqual, - TokenValue::LessEqual => BinaryOperator::LessEqual, - TokenValue::LeftAngle => BinaryOperator::Less, - TokenValue::RightAngle => BinaryOperator::Greater, - TokenValue::LeftShift => BinaryOperator::ShiftLeft, - TokenValue::RightShift => BinaryOperator::ShiftRight, - TokenValue::Plus => BinaryOperator::Add, - TokenValue::Dash => BinaryOperator::Subtract, - TokenValue::Star => BinaryOperator::Multiply, - TokenValue::Slash => BinaryOperator::Divide, - TokenValue::Percent => BinaryOperator::Modulo, - _ => unreachable!(), - }, - right, - }, - meta: start_meta.union(&end_meta), - }) - } - - Ok(left) - } - - fn parse_conditional( - &mut self, - ctx: &mut Context, - body: &mut Block, - passtrough: Option>, - ) -> Result> { - let mut condition = self.parse_binary(ctx, body, passtrough, 0)?; - let start_meta = ctx.hir_exprs[condition].meta; - - if self.bump_if(TokenValue::Question).is_some() { - let accept = self.parse_expression(ctx, body)?; - self.expect(TokenValue::Colon)?; - let reject = self.parse_assignment(ctx, body)?; - let end_meta = ctx.hir_exprs[reject].meta; - - condition = ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Conditional { - condition, - accept, - reject, - }, - meta: start_meta.union(&end_meta), - }) - } - - Ok(condition) - } - - fn parse_assignment(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - let tgt = self.parse_unary(ctx, body)?; - let start_meta = ctx.hir_exprs[tgt].meta; - - Ok(match self.expect_peek()?.value { - TokenValue::Assign => { - self.bump()?; - let value = self.parse_assignment(ctx, body)?; - let end_meta = ctx.hir_exprs[value].meta; - - ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Assign { tgt, value }, - meta: start_meta.union(&end_meta), - }) - } - TokenValue::OrAssign - | TokenValue::AndAssign - | TokenValue::AddAssign - | TokenValue::DivAssign - | TokenValue::ModAssign - | TokenValue::SubAssign - | TokenValue::MulAssign - | TokenValue::LeftShiftAssign - | TokenValue::RightShiftAssign - | TokenValue::XorAssign => { - let token = self.bump()?; - let right = self.parse_assignment(ctx, body)?; - let end_meta = ctx.hir_exprs[right].meta; - - let value = ctx.hir_exprs.append(HirExpr { - meta: start_meta.union(&end_meta), - kind: HirExprKind::Binary { - left: tgt, - op: match token.value { - TokenValue::OrAssign => BinaryOperator::InclusiveOr, - TokenValue::AndAssign => BinaryOperator::And, - TokenValue::AddAssign => BinaryOperator::Add, - TokenValue::DivAssign => BinaryOperator::Divide, - TokenValue::ModAssign => BinaryOperator::Modulo, - TokenValue::SubAssign => BinaryOperator::Subtract, - TokenValue::MulAssign => BinaryOperator::Multiply, - TokenValue::LeftShiftAssign => BinaryOperator::ShiftLeft, - TokenValue::RightShiftAssign => BinaryOperator::ShiftRight, - TokenValue::XorAssign => BinaryOperator::ExclusiveOr, - _ => unreachable!(), - }, - right, - }, - }); - - ctx.hir_exprs.append(HirExpr { - kind: HirExprKind::Assign { tgt, value }, - meta: start_meta.union(&end_meta), - }) - } - _ => self.parse_conditional(ctx, body, Some(tgt))?, - }) - } - - fn parse_expression(&mut self, ctx: &mut Context, body: &mut Block) -> Result> { - let mut expr = self.parse_assignment(ctx, body)?; - - while let TokenValue::Comma = self.expect_peek()?.value { - self.bump()?; - expr = self.parse_assignment(ctx, body)?; - } - - Ok(expr) - } - - fn parse_statement(&mut self, ctx: &mut Context, body: &mut Block) -> Result<()> { - // TODO: This prevents snippets like the following from working - // ```glsl - // vec4(1.0); - // ``` - // But this would require us to add lookahead to also support - // declarations and since this statement is very unlikely and most - // likely an error, for now we don't support it - if self.peek_type_name() || self.peek_type_qualifier() { - self.parse_declaration(ctx, body, false)?; - return Ok(()); - } - - match self.expect_peek()?.value { - TokenValue::Continue => { - self.bump()?; - body.push(Statement::Continue); - self.expect(TokenValue::Semicolon)?; - } - TokenValue::Break => { - self.bump()?; - body.push(Statement::Break); - self.expect(TokenValue::Semicolon)?; - } - TokenValue::Return => { - self.bump()?; - let value = match self.expect_peek()?.value { - TokenValue::Semicolon => { - self.bump()?; - None - } - _ => { - // TODO: Implicit conversions - let expr = self.parse_expression(ctx, body)?; - self.expect(TokenValue::Semicolon)?; - Some(ctx.lower_expect(self.program, expr, false, body)?.0) - } - }; - - ctx.emit_flush(body); - ctx.emit_start(); - - body.push(Statement::Return { value }) - } - TokenValue::Discard => { - self.bump()?; - body.push(Statement::Kill); - self.expect(TokenValue::Semicolon)?; - } - TokenValue::If => { - self.bump()?; - - self.expect(TokenValue::LeftParen)?; - let condition = { - let expr = self.parse_expression(ctx, body)?; - ctx.lower_expect(self.program, expr, false, body)?.0 - }; - self.expect(TokenValue::RightParen)?; - - ctx.emit_flush(body); - ctx.emit_start(); - - let mut accept = Block::new(); - self.parse_statement(ctx, &mut accept)?; - - let mut reject = Block::new(); - if self.bump_if(TokenValue::Else).is_some() { - self.parse_statement(ctx, &mut reject)?; - } - - body.push(Statement::If { - condition, - accept, - reject, - }); - } - TokenValue::Switch => { - self.bump()?; - - self.expect(TokenValue::LeftParen)?; - // TODO: Implicit conversions - let selector = { - let expr = self.parse_expression(ctx, body)?; - ctx.lower_expect(self.program, expr, false, body)?.0 - }; - self.expect(TokenValue::RightParen)?; - - ctx.emit_flush(body); - ctx.emit_start(); - - let mut cases = Vec::new(); - let mut default = Block::new(); - - self.expect(TokenValue::LeftBrace)?; - loop { - match self.expect_peek()?.value { - TokenValue::Case => { - self.bump()?; - let value = { - let expr = self.parse_expression(ctx, body)?; - let (root, meta) = - ctx.lower_expect(self.program, expr, false, body)?; - let constant = self.program.solve_constant(ctx, root, meta)?; - - match self.program.module.constants[constant].inner { - ConstantInner::Scalar { - value: ScalarValue::Sint(int), - .. - } => int as i32, - ConstantInner::Scalar { - value: ScalarValue::Uint(int), - .. - } => int as i32, - _ => { - return Err(ErrorKind::SemanticError( - meta, - "Case values can only be integers".into(), - )) - } - } - }; - - self.expect(TokenValue::Colon)?; - - let mut body = Block::new(); - - loop { - match self.expect_peek()?.value { - TokenValue::Case - | TokenValue::Default - | TokenValue::RightBrace => break, - _ => self.parse_statement(ctx, &mut body)?, - } - } - - let mut fall_through = true; - - for (i, stmt) in body.iter().enumerate() { - if let Statement::Break = *stmt { - fall_through = false; - body.drain(i..); - break; - } - } - - cases.push(SwitchCase { - value, - body, - fall_through, - }) - } - TokenValue::Default => { - let Token { meta, .. } = self.bump()?; - self.expect(TokenValue::Colon)?; - - if !default.is_empty() { - return Err(ErrorKind::SemanticError( - meta, - "Can only have one default case per switch statement".into(), - )); - } - - loop { - match self.expect_peek()?.value { - TokenValue::Case | TokenValue::RightBrace => break, - _ => self.parse_statement(ctx, &mut default)?, - } - } - - for (i, stmt) in default.iter().enumerate() { - if let Statement::Break = *stmt { - default.drain(i..); - break; - } - } - } - TokenValue::RightBrace => { - self.bump()?; - break; - } - _ => { - return Err(ErrorKind::InvalidToken( - self.bump()?, - vec![ - TokenValue::Case.into(), - TokenValue::Default.into(), - TokenValue::RightBrace.into(), - ], - )) - } - } - } - - body.push(Statement::Switch { - selector, - cases, - default, - }); - } - TokenValue::While => { - self.bump()?; - - let mut loop_body = Block::new(); - - self.expect(TokenValue::LeftParen)?; - let root = self.parse_expression(ctx, &mut loop_body)?; - self.expect(TokenValue::RightParen)?; - - let expr = ctx - .lower_expect(self.program, root, false, &mut loop_body)? - .0; - let condition = ctx.add_expression( - Expression::Unary { - op: UnaryOperator::Not, - expr, - }, - &mut loop_body, - ); - - ctx.emit_flush(&mut loop_body); - ctx.emit_start(); - - loop_body.push(Statement::If { - condition, - accept: vec![Statement::Break], - reject: Block::new(), - }); - - self.parse_statement(ctx, &mut loop_body)?; - - body.push(Statement::Loop { - body: loop_body, - continuing: Block::new(), - }) - } - TokenValue::Do => { - self.bump()?; - - let mut loop_body = Block::new(); - self.parse_statement(ctx, &mut loop_body)?; - - self.expect(TokenValue::While)?; - self.expect(TokenValue::LeftParen)?; - let root = self.parse_expression(ctx, &mut loop_body)?; - self.expect(TokenValue::RightParen)?; - - let expr = ctx - .lower_expect(self.program, root, false, &mut loop_body)? - .0; - let condition = ctx.add_expression( - Expression::Unary { - op: UnaryOperator::Not, - expr, - }, - &mut loop_body, - ); - - ctx.emit_flush(&mut loop_body); - ctx.emit_start(); - - loop_body.push(Statement::If { - condition, - accept: vec![Statement::Break], - reject: Block::new(), - }); - - body.push(Statement::Loop { - body: loop_body, - continuing: Block::new(), - }) - } - TokenValue::For => { - self.bump()?; - - ctx.push_scope(); - self.expect(TokenValue::LeftParen)?; - - if self.bump_if(TokenValue::Semicolon).is_none() { - if self.peek_type_name() || self.peek_type_qualifier() { - self.parse_declaration(ctx, body, false)?; - } else { - let expr = self.parse_expression(ctx, body)?; - ctx.lower(self.program, expr, false, body)?; - self.expect(TokenValue::Semicolon)?; - } - } - - let (mut block, mut continuing) = (Block::new(), Block::new()); - - if self.bump_if(TokenValue::Semicolon).is_none() { - let expr = if self.peek_type_name() || self.peek_type_qualifier() { - let qualifiers = self.parse_type_qualifiers()?; - let (ty, meta) = self.parse_type_non_void()?; - let name = self.expect_ident()?.0; - - self.expect(TokenValue::Assign)?; - - let (value, end_meta) = self.parse_initializer(ty, ctx, &mut block)?; - - let decl = VarDeclaration { - qualifiers: &qualifiers, - ty, - name: Some(name), - init: None, - meta: meta.union(&end_meta), - }; - - let pointer = self.program.add_local_var(ctx, &mut block, decl)?; - - ctx.emit_flush(&mut block); - ctx.emit_start(); - - block.push(Statement::Store { pointer, value }); - - value - } else { - let root = self.parse_expression(ctx, &mut block)?; - ctx.lower_expect(self.program, root, false, &mut block)?.0 - }; - - let condition = ctx.add_expression( - Expression::Unary { - op: UnaryOperator::Not, - expr, - }, - &mut block, - ); - - ctx.emit_flush(&mut block); - ctx.emit_start(); - - block.push(Statement::If { - condition, - accept: vec![Statement::Break], - reject: Block::new(), - }); - - self.expect(TokenValue::Semicolon)?; - } - - match self.expect_peek()?.value { - TokenValue::RightParen => {} - _ => { - let rest = self.parse_expression(ctx, &mut continuing)?; - ctx.lower(self.program, rest, false, &mut continuing)?; - } - } - - self.expect(TokenValue::RightParen)?; - - self.parse_statement(ctx, &mut block)?; - - body.push(Statement::Loop { - body: block, - continuing, - }); - - ctx.remove_current_scope(); - } - TokenValue::LeftBrace => { - self.bump()?; - - let mut block = Block::new(); - ctx.push_scope(); - - self.parse_compound_statement(ctx, &mut block)?; - - ctx.remove_current_scope(); - body.push(Statement::Block(block)); - } - TokenValue::Plus - | TokenValue::Dash - | TokenValue::Bang - | TokenValue::Tilde - | TokenValue::LeftParen - | TokenValue::Identifier(_) - | TokenValue::TypeName(_) - | TokenValue::IntConstant(_) - | TokenValue::BoolConstant(_) - | TokenValue::FloatConstant(_) => { - let expr = self.parse_expression(ctx, body)?; - ctx.lower(self.program, expr, false, body)?; - self.expect(TokenValue::Semicolon)?; - } - TokenValue::Semicolon => { - self.bump()?; - } - _ => {} - } - - Ok(()) - } - - fn parse_compound_statement(&mut self, ctx: &mut Context, body: &mut Block) -> Result<()> { - loop { - if self.bump_if(TokenValue::RightBrace).is_some() { - break; - } - - self.parse_statement(ctx, body)?; - } - - Ok(()) - } - - fn parse_function_args( - &mut self, - context: &mut Context, - body: &mut Block, - parameters: &mut Vec>, - ) -> Result<()> { - loop { - if self.peek_type_name() || self.peek_parameter_qualifier() { - let qualifier = self.parse_parameter_qualifier(); - let ty = self.parse_type_non_void()?.0; - - match self.expect_peek()?.value { - TokenValue::Comma => { - self.bump()?; - context.add_function_arg( - &mut self.program, - parameters, - body, - None, - ty, - qualifier, - ); - continue; - } - TokenValue::Identifier(_) => { - let name = self.expect_ident()?.0; - - let size = self.parse_array_specifier()?; - let ty = self.maybe_array(ty, size); - - context.add_function_arg( - &mut self.program, - parameters, - body, - Some(name), - ty, - qualifier, - ); - - if self.bump_if(TokenValue::Comma).is_some() { - continue; - } - - break; - } - _ => break, - } - } - - break; - } - - Ok(()) + Ok((parser.solve_constant(&ctx, root, meta)?, meta)) } } -struct DeclarationContext<'ctx, 'fun> { +pub struct DeclarationContext<'ctx> { qualifiers: Vec<(TypeQualifier, SourceMetadata)>, external: bool, - ctx: &'ctx mut Context<'fun>, + ctx: &'ctx mut Context, body: &'ctx mut Block, } -impl<'ctx, 'fun> DeclarationContext<'ctx, 'fun> { +impl<'ctx> DeclarationContext<'ctx> { fn add_var( &mut self, - program: &mut Program, + parser: &mut Parser, ty: Handle, name: String, init: Option>, @@ -1981,14 +208,14 @@ impl<'ctx, 'fun> DeclarationContext<'ctx, 'fun> { match self.external { true => { - let global = program.add_global_var(self.ctx, self.body, decl)?; + let global = parser.add_global_var(self.ctx, self.body, decl)?; let expr = match global { GlobalOrConstant::Global(handle) => Expression::GlobalVariable(handle), GlobalOrConstant::Constant(handle) => Expression::Constant(handle), }; Ok(self.ctx.add_expression(expr, self.body)) } - false => program.add_local_var(self.ctx, self.body, decl), + false => parser.add_local_var(self.ctx, self.body, decl), } } @@ -1997,23 +224,3 @@ impl<'ctx, 'fun> DeclarationContext<'ctx, 'fun> { self.ctx.emit_start() } } - -fn binding_power(value: &TokenValue) -> Option<(u8, u8)> { - Some(match *value { - TokenValue::LogicalOr => (1, 2), - TokenValue::LogicalXor => (3, 4), - TokenValue::LogicalAnd => (5, 6), - TokenValue::VerticalBar => (7, 8), - TokenValue::Caret => (9, 10), - TokenValue::Ampersand => (11, 12), - TokenValue::Equal | TokenValue::NotEqual => (13, 14), - TokenValue::GreaterEqual - | TokenValue::LessEqual - | TokenValue::LeftAngle - | TokenValue::RightAngle => (15, 16), - TokenValue::LeftShift | TokenValue::RightShift => (17, 18), - TokenValue::Plus | TokenValue::Dash => (19, 20), - TokenValue::Star | TokenValue::Slash | TokenValue::Percent => (21, 22), - _ => return None, - }) -} diff --git a/src/front/glsl/parser/declarations.rs b/src/front/glsl/parser/declarations.rs new file mode 100644 index 0000000000..f74cd15ae3 --- /dev/null +++ b/src/front/glsl/parser/declarations.rs @@ -0,0 +1,581 @@ +use crate::{ + front::glsl::{ + ast::{ + GlobalLookup, GlobalLookupKind, Precision, StorageQualifier, StructLayout, + TypeQualifier, + }, + context::Context, + error::ExpectedToken, + offset, + token::TokenValue, + types::scalar_components, + variables::{GlobalOrConstant, VarDeclaration}, + ErrorKind, Parser, SourceMetadata, Token, + }, + Block, Expression, FunctionResult, Handle, ScalarKind, Statement, StorageClass, StructMember, + Type, TypeInner, +}; + +use super::{DeclarationContext, ParsingContext, Result}; + +impl<'source> ParsingContext<'source> { + pub fn parse_external_declaration( + &mut self, + parser: &mut Parser, + global_ctx: &mut Context, + global_body: &mut Block, + ) -> Result<()> { + if !self.parse_declaration(parser, global_ctx, global_body, true)? { + let token = self.bump(parser)?; + match token.value { + TokenValue::Semicolon if parser.meta.version == 460 => Ok(()), + _ => { + let expected = match parser.meta.version { + 460 => vec![TokenValue::Semicolon.into(), ExpectedToken::Eof], + _ => vec![ExpectedToken::Eof], + }; + Err(ErrorKind::InvalidToken(token, expected)) + } + } + } else { + Ok(()) + } + } + + pub fn parse_initializer( + &mut self, + parser: &mut Parser, + ty: Handle, + ctx: &mut Context, + body: &mut Block, + ) -> Result<(Handle, SourceMetadata)> { + // initializer: + // assignment_expression + // LEFT_BRACE initializer_list RIGHT_BRACE + // LEFT_BRACE initializer_list COMMA RIGHT_BRACE + // + // initializer_list: + // initializer + // initializer_list COMMA initializer + if let Some(Token { mut meta, .. }) = self.bump_if(parser, TokenValue::LeftBrace) { + // initializer_list + let mut components = Vec::new(); + loop { + // TODO: Change type + components.push(self.parse_initializer(parser, ty, ctx, body)?.0); + + let token = self.bump(parser)?; + match token.value { + TokenValue::Comma => { + if let Some(Token { meta: end_meta, .. }) = + self.bump_if(parser, TokenValue::RightBrace) + { + meta = meta.union(&end_meta); + break; + } + } + TokenValue::RightBrace => { + meta = meta.union(&token.meta); + break; + } + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![TokenValue::Comma.into(), TokenValue::RightBrace.into()], + )) + } + } + } + + Ok(( + ctx.add_expression(Expression::Compose { ty, components }, body), + meta, + )) + } else { + let expr = self.parse_assignment(parser, ctx, body)?; + let (mut init, init_meta) = ctx.lower_expect(parser, expr, false, body)?; + + let scalar_components = scalar_components(&parser.module.types[ty].inner); + if let Some((kind, width)) = scalar_components { + ctx.implicit_conversion(parser, &mut init, init_meta, kind, width)?; + } + + Ok((init, init_meta)) + } + } + + // Note: caller preparsed the type and qualifiers + // Note: caller skips this if the fallthrough token is not expected to be consumed here so this + // produced Error::InvalidToken if it isn't consumed + pub fn parse_init_declarator_list( + &mut self, + parser: &mut Parser, + ty: Handle, + mut fallthrough: Option, + ctx: &mut DeclarationContext, + ) -> Result<()> { + // init_declarator_list: + // single_declaration + // init_declarator_list COMMA IDENTIFIER + // init_declarator_list COMMA IDENTIFIER array_specifier + // init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer + // init_declarator_list COMMA IDENTIFIER EQUAL initializer + // + // single_declaration: + // fully_specified_type + // fully_specified_type IDENTIFIER + // fully_specified_type IDENTIFIER array_specifier + // fully_specified_type IDENTIFIER array_specifier EQUAL initializer + // fully_specified_type IDENTIFIER EQUAL initializer + + // Consume any leading comma, e.g. this is valid: `float, a=1;` + if fallthrough + .as_ref() + .or_else(|| self.peek(parser)) + .filter(|t| t.value == TokenValue::Comma) + .is_some() + { + fallthrough.take().or_else(|| self.next(parser)); + } + + loop { + let token = fallthrough + .take() + .ok_or(ErrorKind::EndOfFile) + .or_else(|_| self.bump(parser))?; + let name = match token.value { + TokenValue::Semicolon => break, + TokenValue::Identifier(name) => name, + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], + )) + } + }; + let mut meta = token.meta; + + // array_specifier + // array_specifier EQUAL initializer + // EQUAL initializer + + // parse an array specifier if it exists + // NOTE: unlike other parse methods this one doesn't expect an array specifier and + // returns Ok(None) rather than an error if there is not one + let array_specifier = self.parse_array_specifier(parser)?; + let ty = parser.maybe_array(ty, array_specifier); + + let init = self + .bump_if(parser, TokenValue::Assign) + .map::, _>(|_| { + let (mut expr, init_meta) = + self.parse_initializer(parser, ty, ctx.ctx, ctx.body)?; + + let scalar_components = scalar_components(&parser.module.types[ty].inner); + if let Some((kind, width)) = scalar_components { + ctx.ctx + .implicit_conversion(parser, &mut expr, init_meta, kind, width)?; + } + + meta = meta.union(&init_meta); + + Ok((expr, init_meta)) + }) + .transpose()?; + + // TODO: Should we try to make constants here? + // This is mostly a hack because we don't yet support adding + // bodies to entry points for variable initialization + let maybe_constant = + init.and_then(|(root, meta)| parser.solve_constant(ctx.ctx, root, meta).ok()); + + let pointer = ctx.add_var(parser, ty, name, maybe_constant, meta)?; + + if let Some((value, _)) = init.filter(|_| maybe_constant.is_none()) { + ctx.flush_expressions(); + ctx.body.push(Statement::Store { pointer, value }); + } + + let token = self.bump(parser)?; + match token.value { + TokenValue::Semicolon => break, + TokenValue::Comma => {} + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![TokenValue::Comma.into(), TokenValue::Semicolon.into()], + )) + } + } + } + + Ok(()) + } + + /// `external` whether or not we are in a global or local context + pub fn parse_declaration( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + external: bool, + ) -> Result { + //declaration: + // function_prototype SEMICOLON + // + // init_declarator_list SEMICOLON + // PRECISION precision_qualifier type_specifier SEMICOLON + // + // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE SEMICOLON + // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON + // type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER array_specifier SEMICOLON + // type_qualifier SEMICOLON type_qualifier IDENTIFIER SEMICOLON + // type_qualifier IDENTIFIER identifier_list SEMICOLON + + if self.peek_type_qualifier(parser) || self.peek_type_name(parser) { + let qualifiers = self.parse_type_qualifiers(parser)?; + + if self.peek_type_name(parser) { + // This branch handles variables and function prototypes and if + // external is true also function definitions + let (ty, mut meta) = self.parse_type(parser)?; + + let token = self.bump(parser)?; + let token_fallthrough = match token.value { + TokenValue::Identifier(name) => match self.expect_peek(parser)?.value { + TokenValue::LeftParen => { + // This branch handles function definition and prototypes + self.bump(parser)?; + + let result = ty.map(|ty| FunctionResult { ty, binding: None }); + let mut body = Block::new(); + + let mut context = Context::new(parser, &mut body); + + self.parse_function_args(parser, &mut context, &mut body)?; + + let end_meta = self.expect(parser, TokenValue::RightParen)?.meta; + meta = meta.union(&end_meta); + + let token = self.bump(parser)?; + return match token.value { + TokenValue::Semicolon => { + // This branch handles function prototypes + parser.add_prototype(context, name, result, meta)?; + + Ok(true) + } + TokenValue::LeftBrace if external => { + // This branch handles function definitions + // as you can see by the guard this branch + // only happens if external is also true + + // parse the body + self.parse_compound_statement(parser, &mut context, &mut body)?; + + parser.add_function(context, name, result, body, meta)?; + + Ok(true) + } + _ if external => Err(ErrorKind::InvalidToken( + token, + vec![ + TokenValue::LeftBrace.into(), + TokenValue::Semicolon.into(), + ], + )), + _ => Err(ErrorKind::InvalidToken( + token, + vec![TokenValue::Semicolon.into()], + )), + }; + } + // Pass the token to the init_declator_list parser + _ => Token { + value: TokenValue::Identifier(name), + meta: token.meta, + }, + }, + // Pass the token to the init_declator_list parser + _ => token, + }; + + // If program execution has reached here then this will be a + // init_declarator_list + // token_falltrough will have a token that was already bumped + if let Some(ty) = ty { + let mut ctx = DeclarationContext { + qualifiers, + external, + ctx, + body, + }; + + self.parse_init_declarator_list(parser, ty, Some(token_fallthrough), &mut ctx)?; + } else { + return Err(ErrorKind::SemanticError( + meta, + "Declaration cannot have void type".into(), + )); + } + + Ok(true) + } else { + // This branch handles struct definitions and modifiers like + // ```glsl + // layout(early_fragment_tests); + // ``` + let token = self.bump(parser)?; + match token.value { + TokenValue::Identifier(ty_name) => { + if self.bump_if(parser, TokenValue::LeftBrace).is_some() { + self.parse_block_declaration( + parser, + ctx, + body, + &qualifiers, + ty_name, + token.meta, + ) + } else { + //TODO: declaration + // type_qualifier IDENTIFIER SEMICOLON + // type_qualifier IDENTIFIER identifier_list SEMICOLON + Err(ErrorKind::NotImplemented(token.meta, "variable qualifier")) + } + } + TokenValue::Semicolon => { + for &(ref qualifier, meta) in qualifiers.iter() { + match *qualifier { + TypeQualifier::WorkGroupSize(i, value) => { + parser.meta.workgroup_size[i] = value + } + TypeQualifier::EarlyFragmentTests => { + parser.meta.early_fragment_tests = true; + } + TypeQualifier::StorageQualifier(_) => { + // TODO: Maybe add some checks here + // This is needed because of cases like + // layout(early_fragment_tests) in; + } + _ => { + return Err(ErrorKind::SemanticError( + meta, + "Qualifier not supported as standalone".into(), + )); + } + } + } + + Ok(true) + } + _ => Err(ErrorKind::InvalidToken( + token, + vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], + )), + } + } + } else { + match self.peek(parser).map(|t| &t.value) { + Some(&TokenValue::Precision) => { + // PRECISION precision_qualifier type_specifier SEMICOLON + self.bump(parser)?; + + let token = self.bump(parser)?; + let _ = match token.value { + TokenValue::PrecisionQualifier(p) => p, + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![ + TokenValue::PrecisionQualifier(Precision::High).into(), + TokenValue::PrecisionQualifier(Precision::Medium).into(), + TokenValue::PrecisionQualifier(Precision::Low).into(), + ], + )) + } + }; + + let (ty, meta) = self.parse_type_non_void(parser)?; + + match parser.module.types[ty].inner { + TypeInner::Scalar { + kind: ScalarKind::Float, + .. + } + | TypeInner::Scalar { + kind: ScalarKind::Sint, + .. + } => {} + _ => { + return Err(ErrorKind::SemanticError( + meta, + "Precision statement can only work on floats and ints".into(), + )) + } + } + + self.expect(parser, TokenValue::Semicolon)?; + + Ok(true) + } + _ => Ok(false), + } + } + } + + pub fn parse_block_declaration( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + qualifiers: &[(TypeQualifier, SourceMetadata)], + ty_name: String, + mut meta: SourceMetadata, + ) -> Result { + let mut storage = None; + let mut layout = None; + + for &(ref qualifier, _) in qualifiers { + match *qualifier { + TypeQualifier::StorageQualifier(StorageQualifier::StorageClass(c)) => { + storage = Some(c) + } + TypeQualifier::Layout(l) => layout = Some(l), + _ => continue, + } + } + + let layout = match (layout, storage) { + (Some(layout), _) => layout, + (None, Some(StorageClass::Storage { .. })) => StructLayout::Std430, + _ => StructLayout::Std140, + }; + + let mut members = Vec::new(); + let span = self.parse_struct_declaration_list(parser, &mut members, layout)?; + self.expect(parser, TokenValue::RightBrace)?; + + let mut ty = parser.module.types.append(Type { + name: Some(ty_name), + inner: TypeInner::Struct { + top_level: true, + members: members.clone(), + span, + }, + }); + + let token = self.bump(parser)?; + let name = match token.value { + TokenValue::Semicolon => None, + TokenValue::Identifier(name) => { + if let Some(size) = self.parse_array_specifier(parser)? { + ty = parser.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Array { + base: ty, + size, + stride: parser.module.types[ty].inner.span(&parser.module.constants), + }, + }); + } + + self.expect(parser, TokenValue::Semicolon)?; + + Some(name) + } + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()], + )) + } + }; + meta = meta.union(&token.meta); + + let global = parser.add_global_var( + ctx, + body, + VarDeclaration { + qualifiers, + ty, + name, + init: None, + meta, + }, + )?; + + for (i, k) in members + .into_iter() + .enumerate() + .filter_map(|(i, m)| m.name.map(|s| (i as u32, s))) + { + let lookup = GlobalLookup { + kind: match global { + GlobalOrConstant::Global(handle) => GlobalLookupKind::BlockSelect(handle, i), + GlobalOrConstant::Constant(handle) => GlobalLookupKind::Constant(handle), + }, + entry_arg: None, + mutable: true, + }; + ctx.add_global(parser, &k, lookup, body); + + parser.global_variables.push((k, lookup)); + } + + Ok(true) + } + + // TODO: Accept layout arguments + pub fn parse_struct_declaration_list( + &mut self, + parser: &mut Parser, + members: &mut Vec, + layout: StructLayout, + ) -> Result { + let mut span = 0; + let mut align = 0; + + loop { + // TODO: type_qualifier + + let (ty, mut meta) = self.parse_type_non_void(parser)?; + let (name, end_meta) = self.expect_ident(parser)?; + + meta = meta.union(&end_meta); + + let array_specifier = self.parse_array_specifier(parser)?; + let ty = parser.maybe_array(ty, array_specifier); + + self.expect(parser, TokenValue::Semicolon)?; + + let info = offset::calculate_offset( + ty, + meta, + layout, + &mut parser.module.types, + &parser.module.constants, + )?; + + span = offset::align_up(span, info.align); + align = align.max(info.align); + + members.push(StructMember { + name: Some(name), + ty: info.ty, + binding: None, + offset: span, + }); + + span += info.span; + + if let TokenValue::RightBrace = self.expect_peek(parser)?.value { + break; + } + } + + span = offset::align_up(span, align); + + Ok(span) + } +} diff --git a/src/front/glsl/parser/expressions.rs b/src/front/glsl/parser/expressions.rs new file mode 100644 index 0000000000..8f119eaaae --- /dev/null +++ b/src/front/glsl/parser/expressions.rs @@ -0,0 +1,486 @@ +use crate::{ + front::glsl::{ + ast::{FunctionCall, FunctionCallKind, HirExpr, HirExprKind}, + context::Context, + error::{ErrorKind, ExpectedToken}, + parser::ParsingContext, + token::{Token, TokenValue}, + Parser, Result, SourceMetadata, + }, + ArraySize, BinaryOperator, Block, Constant, ConstantInner, Handle, ScalarValue, Type, + TypeInner, UnaryOperator, +}; + +impl<'source> ParsingContext<'source> { + pub fn parse_primary( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + ) -> Result> { + let mut token = self.bump(parser)?; + + let (width, value) = match token.value { + TokenValue::IntConstant(int) => ( + (int.width / 8) as u8, + if int.signed { + ScalarValue::Sint(int.value as i64) + } else { + ScalarValue::Uint(int.value) + }, + ), + TokenValue::FloatConstant(float) => ( + (float.width / 8) as u8, + ScalarValue::Float(float.value as f64), + ), + TokenValue::BoolConstant(value) => (1, ScalarValue::Bool(value)), + TokenValue::LeftParen => { + let expr = self.parse_expression(parser, ctx, body)?; + let meta = self.expect(parser, TokenValue::RightParen)?.meta; + + token.meta = token.meta.union(&meta); + + return Ok(expr); + } + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![ + TokenValue::LeftParen.into(), + ExpectedToken::IntLiteral, + ExpectedToken::FloatLiteral, + ExpectedToken::BoolLiteral, + ], + )) + } + }; + + let handle = parser.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + inner: ConstantInner::Scalar { width, value }, + }); + + Ok(ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Constant(handle), + meta: token.meta, + })) + } + + pub fn parse_function_call_args( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + meta: &mut SourceMetadata, + ) -> Result>> { + let mut args = Vec::new(); + if let Some(token) = self.bump_if(parser, TokenValue::RightParen) { + *meta = meta.union(&token.meta); + } else { + loop { + args.push(self.parse_assignment(parser, ctx, body)?); + + let token = self.bump(parser)?; + match token.value { + TokenValue::Comma => {} + TokenValue::RightParen => { + *meta = meta.union(&token.meta); + break; + } + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![TokenValue::Comma.into(), TokenValue::RightParen.into()], + )) + } + } + } + } + + Ok(args) + } + + pub fn parse_postfix( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + ) -> Result> { + let mut base = match self.expect_peek(parser)?.value { + TokenValue::Identifier(_) => { + let (name, mut meta) = self.expect_ident(parser)?; + + let expr = if self.bump_if(parser, TokenValue::LeftParen).is_some() { + let args = self.parse_function_call_args(parser, ctx, body, &mut meta)?; + + let kind = match parser.lookup_type.get(&name) { + Some(ty) => FunctionCallKind::TypeConstructor(*ty), + None => FunctionCallKind::Function(name), + }; + + HirExpr { + kind: HirExprKind::Call(FunctionCall { kind, args }), + meta, + } + } else { + let var = match parser.lookup_variable(ctx, body, &name)? { + Some(var) => var, + None => return Err(ErrorKind::UnknownVariable(meta, name)), + }; + + HirExpr { + kind: HirExprKind::Variable(var), + meta, + } + }; + + ctx.hir_exprs.append(expr) + } + TokenValue::TypeName(_) => { + let Token { value, mut meta } = self.bump(parser)?; + + let mut handle = if let TokenValue::TypeName(ty) = value { + parser.module.types.fetch_or_append(ty) + } else { + unreachable!() + }; + + let maybe_size = self.parse_array_specifier(parser)?; + + self.expect(parser, TokenValue::LeftParen)?; + let args = self.parse_function_call_args(parser, ctx, body, &mut meta)?; + + if let Some(array_size) = maybe_size { + let stride = parser.module.types[handle] + .inner + .span(&parser.module.constants); + + let size = match array_size { + ArraySize::Constant(size) => ArraySize::Constant(size), + ArraySize::Dynamic => { + let constant = parser.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + inner: ConstantInner::Scalar { + width: 4, + value: ScalarValue::Sint(args.len() as i64), + }, + }); + + ArraySize::Constant(constant) + } + }; + + handle = parser.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Array { + base: handle, + size, + stride, + }, + }) + } + + ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Call(FunctionCall { + kind: FunctionCallKind::TypeConstructor(handle), + args, + }), + meta, + }) + } + _ => self.parse_primary(parser, ctx, body)?, + }; + + while let TokenValue::LeftBracket + | TokenValue::Dot + | TokenValue::Increment + | TokenValue::Decrement = self.expect_peek(parser)?.value + { + let Token { value, meta } = self.bump(parser)?; + + match value { + TokenValue::LeftBracket => { + let index = self.parse_expression(parser, ctx, body)?; + let end_meta = self.expect(parser, TokenValue::RightBracket)?.meta; + + base = ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Access { base, index }, + meta: meta.union(&end_meta), + }) + } + TokenValue::Dot => { + let (field, end_meta) = self.expect_ident(parser)?; + + base = ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Select { base, field }, + meta: meta.union(&end_meta), + }) + } + TokenValue::Increment => { + base = ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::IncDec { + increment: true, + postfix: true, + expr: base, + }, + meta, + }) + } + TokenValue::Decrement => { + base = ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::IncDec { + increment: false, + postfix: true, + expr: base, + }, + meta, + }) + } + _ => unreachable!(), + } + } + + Ok(base) + } + + pub fn parse_unary( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + ) -> Result> { + // TODO: prefix inc/dec + Ok(match self.expect_peek(parser)?.value { + TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => { + let Token { value, meta } = self.bump(parser)?; + + let expr = self.parse_unary(parser, ctx, body)?; + let end_meta = ctx.hir_exprs[expr].meta; + + let kind = match value { + TokenValue::Dash => HirExprKind::Unary { + op: UnaryOperator::Negate, + expr, + }, + TokenValue::Bang | TokenValue::Tilde => HirExprKind::Unary { + op: UnaryOperator::Not, + expr, + }, + _ => return Ok(expr), + }; + + ctx.hir_exprs.append(HirExpr { + kind, + meta: meta.union(&end_meta), + }) + } + TokenValue::Increment | TokenValue::Decrement => { + let Token { value, meta } = self.bump(parser)?; + + let expr = self.parse_unary(parser, ctx, body)?; + + ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::IncDec { + increment: match value { + TokenValue::Increment => true, + TokenValue::Decrement => false, + _ => unreachable!(), + }, + postfix: false, + expr, + }, + meta, + }) + } + _ => self.parse_postfix(parser, ctx, body)?, + }) + } + + pub fn parse_binary( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + passtrough: Option>, + min_bp: u8, + ) -> Result> { + let mut left = passtrough + .ok_or(ErrorKind::EndOfFile /* Dummy error */) + .or_else(|_| self.parse_unary(parser, ctx, body))?; + let start_meta = ctx.hir_exprs[left].meta; + + while let Some((l_bp, r_bp)) = binding_power(&self.expect_peek(parser)?.value) { + if l_bp < min_bp { + break; + } + + let Token { value, .. } = self.bump(parser)?; + + let right = self.parse_binary(parser, ctx, body, None, r_bp)?; + let end_meta = ctx.hir_exprs[right].meta; + + left = ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Binary { + left, + op: match value { + TokenValue::LogicalOr => BinaryOperator::LogicalOr, + TokenValue::LogicalXor => BinaryOperator::NotEqual, + TokenValue::LogicalAnd => BinaryOperator::LogicalAnd, + TokenValue::VerticalBar => BinaryOperator::InclusiveOr, + TokenValue::Caret => BinaryOperator::ExclusiveOr, + TokenValue::Ampersand => BinaryOperator::And, + TokenValue::Equal => BinaryOperator::Equal, + TokenValue::NotEqual => BinaryOperator::NotEqual, + TokenValue::GreaterEqual => BinaryOperator::GreaterEqual, + TokenValue::LessEqual => BinaryOperator::LessEqual, + TokenValue::LeftAngle => BinaryOperator::Less, + TokenValue::RightAngle => BinaryOperator::Greater, + TokenValue::LeftShift => BinaryOperator::ShiftLeft, + TokenValue::RightShift => BinaryOperator::ShiftRight, + TokenValue::Plus => BinaryOperator::Add, + TokenValue::Dash => BinaryOperator::Subtract, + TokenValue::Star => BinaryOperator::Multiply, + TokenValue::Slash => BinaryOperator::Divide, + TokenValue::Percent => BinaryOperator::Modulo, + _ => unreachable!(), + }, + right, + }, + meta: start_meta.union(&end_meta), + }) + } + + Ok(left) + } + + pub fn parse_conditional( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + passtrough: Option>, + ) -> Result> { + let mut condition = self.parse_binary(parser, ctx, body, passtrough, 0)?; + let start_meta = ctx.hir_exprs[condition].meta; + + if self.bump_if(parser, TokenValue::Question).is_some() { + let accept = self.parse_expression(parser, ctx, body)?; + self.expect(parser, TokenValue::Colon)?; + let reject = self.parse_assignment(parser, ctx, body)?; + let end_meta = ctx.hir_exprs[reject].meta; + + condition = ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Conditional { + condition, + accept, + reject, + }, + meta: start_meta.union(&end_meta), + }) + } + + Ok(condition) + } + + pub fn parse_assignment( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + ) -> Result> { + let tgt = self.parse_unary(parser, ctx, body)?; + let start_meta = ctx.hir_exprs[tgt].meta; + + Ok(match self.expect_peek(parser)?.value { + TokenValue::Assign => { + self.bump(parser)?; + let value = self.parse_assignment(parser, ctx, body)?; + let end_meta = ctx.hir_exprs[value].meta; + + ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Assign { tgt, value }, + meta: start_meta.union(&end_meta), + }) + } + TokenValue::OrAssign + | TokenValue::AndAssign + | TokenValue::AddAssign + | TokenValue::DivAssign + | TokenValue::ModAssign + | TokenValue::SubAssign + | TokenValue::MulAssign + | TokenValue::LeftShiftAssign + | TokenValue::RightShiftAssign + | TokenValue::XorAssign => { + let token = self.bump(parser)?; + let right = self.parse_assignment(parser, ctx, body)?; + let end_meta = ctx.hir_exprs[right].meta; + + let value = ctx.hir_exprs.append(HirExpr { + meta: start_meta.union(&end_meta), + kind: HirExprKind::Binary { + left: tgt, + op: match token.value { + TokenValue::OrAssign => BinaryOperator::InclusiveOr, + TokenValue::AndAssign => BinaryOperator::And, + TokenValue::AddAssign => BinaryOperator::Add, + TokenValue::DivAssign => BinaryOperator::Divide, + TokenValue::ModAssign => BinaryOperator::Modulo, + TokenValue::SubAssign => BinaryOperator::Subtract, + TokenValue::MulAssign => BinaryOperator::Multiply, + TokenValue::LeftShiftAssign => BinaryOperator::ShiftLeft, + TokenValue::RightShiftAssign => BinaryOperator::ShiftRight, + TokenValue::XorAssign => BinaryOperator::ExclusiveOr, + _ => unreachable!(), + }, + right, + }, + }); + + ctx.hir_exprs.append(HirExpr { + kind: HirExprKind::Assign { tgt, value }, + meta: start_meta.union(&end_meta), + }) + } + _ => self.parse_conditional(parser, ctx, body, Some(tgt))?, + }) + } + + pub fn parse_expression( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + ) -> Result> { + let mut expr = self.parse_assignment(parser, ctx, body)?; + + while let TokenValue::Comma = self.expect_peek(parser)?.value { + self.bump(parser)?; + expr = self.parse_assignment(parser, ctx, body)?; + } + + Ok(expr) + } +} + +fn binding_power(value: &TokenValue) -> Option<(u8, u8)> { + Some(match *value { + TokenValue::LogicalOr => (1, 2), + TokenValue::LogicalXor => (3, 4), + TokenValue::LogicalAnd => (5, 6), + TokenValue::VerticalBar => (7, 8), + TokenValue::Caret => (9, 10), + TokenValue::Ampersand => (11, 12), + TokenValue::Equal | TokenValue::NotEqual => (13, 14), + TokenValue::GreaterEqual + | TokenValue::LessEqual + | TokenValue::LeftAngle + | TokenValue::RightAngle => (15, 16), + TokenValue::LeftShift | TokenValue::RightShift => (17, 18), + TokenValue::Plus | TokenValue::Dash => (19, 20), + TokenValue::Star | TokenValue::Slash | TokenValue::Percent => (21, 22), + _ => return None, + }) +} diff --git a/src/front/glsl/parser/functions.rs b/src/front/glsl/parser/functions.rs new file mode 100644 index 0000000000..9e1db2b89e --- /dev/null +++ b/src/front/glsl/parser/functions.rs @@ -0,0 +1,483 @@ +use crate::{ + front::glsl::{ + ast::ParameterQualifier, context::Context, parser::ParsingContext, token::TokenValue, + variables::VarDeclaration, ErrorKind, Parser, Result, Token, + }, + Block, ConstantInner, Expression, ScalarValue, Statement, SwitchCase, UnaryOperator, +}; + +impl<'source> ParsingContext<'source> { + pub fn peek_parameter_qualifier(&mut self, parser: &mut Parser) -> bool { + self.peek(parser).map_or(false, |t| match t.value { + TokenValue::In | TokenValue::Out | TokenValue::InOut | TokenValue::Const => true, + _ => false, + }) + } + + /// Returns the parsed `ParameterQualifier` or `ParameterQualifier::In` + pub fn parse_parameter_qualifier(&mut self, parser: &mut Parser) -> ParameterQualifier { + if self.peek_parameter_qualifier(parser) { + match self.bump(parser).unwrap().value { + TokenValue::In => ParameterQualifier::In, + TokenValue::Out => ParameterQualifier::Out, + TokenValue::InOut => ParameterQualifier::InOut, + TokenValue::Const => ParameterQualifier::Const, + _ => unreachable!(), + } + } else { + ParameterQualifier::In + } + } + + pub fn parse_statement( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + ) -> Result<()> { + // TODO: This prevents snippets like the following from working + // ```glsl + // vec4(1.0); + // ``` + // But this would require us to add lookahead to also support + // declarations and since this statement is very unlikely and most + // likely an error, for now we don't support it + if self.peek_type_name(parser) || self.peek_type_qualifier(parser) { + self.parse_declaration(parser, ctx, body, false)?; + return Ok(()); + } + + match self.expect_peek(parser)?.value { + TokenValue::Continue => { + self.bump(parser)?; + body.push(Statement::Continue); + self.expect(parser, TokenValue::Semicolon)?; + } + TokenValue::Break => { + self.bump(parser)?; + body.push(Statement::Break); + self.expect(parser, TokenValue::Semicolon)?; + } + TokenValue::Return => { + self.bump(parser)?; + let value = match self.expect_peek(parser)?.value { + TokenValue::Semicolon => { + self.bump(parser)?; + None + } + _ => { + // TODO: Implicit conversions + let expr = self.parse_expression(parser, ctx, body)?; + self.expect(parser, TokenValue::Semicolon)?; + Some(ctx.lower_expect(parser, expr, false, body)?.0) + } + }; + + ctx.emit_flush(body); + ctx.emit_start(); + + body.push(Statement::Return { value }) + } + TokenValue::Discard => { + self.bump(parser)?; + body.push(Statement::Kill); + self.expect(parser, TokenValue::Semicolon)?; + } + TokenValue::If => { + self.bump(parser)?; + + self.expect(parser, TokenValue::LeftParen)?; + let condition = { + let expr = self.parse_expression(parser, ctx, body)?; + ctx.lower_expect(parser, expr, false, body)?.0 + }; + self.expect(parser, TokenValue::RightParen)?; + + ctx.emit_flush(body); + ctx.emit_start(); + + let mut accept = Block::new(); + self.parse_statement(parser, ctx, &mut accept)?; + + let mut reject = Block::new(); + if self.bump_if(parser, TokenValue::Else).is_some() { + self.parse_statement(parser, ctx, &mut reject)?; + } + + body.push(Statement::If { + condition, + accept, + reject, + }); + } + TokenValue::Switch => { + self.bump(parser)?; + + self.expect(parser, TokenValue::LeftParen)?; + // TODO: Implicit conversions + let selector = { + let expr = self.parse_expression(parser, ctx, body)?; + ctx.lower_expect(parser, expr, false, body)?.0 + }; + self.expect(parser, TokenValue::RightParen)?; + + ctx.emit_flush(body); + ctx.emit_start(); + + let mut cases = Vec::new(); + let mut default = Block::new(); + + self.expect(parser, TokenValue::LeftBrace)?; + loop { + match self.expect_peek(parser)?.value { + TokenValue::Case => { + self.bump(parser)?; + let value = { + let expr = self.parse_expression(parser, ctx, body)?; + let (root, meta) = ctx.lower_expect(parser, expr, false, body)?; + let constant = parser.solve_constant(ctx, root, meta)?; + + match parser.module.constants[constant].inner { + ConstantInner::Scalar { + value: ScalarValue::Sint(int), + .. + } => int as i32, + ConstantInner::Scalar { + value: ScalarValue::Uint(int), + .. + } => int as i32, + _ => { + return Err(ErrorKind::SemanticError( + meta, + "Case values can only be integers".into(), + )) + } + } + }; + + self.expect(parser, TokenValue::Colon)?; + + let mut body = Block::new(); + + loop { + match self.expect_peek(parser)?.value { + TokenValue::Case + | TokenValue::Default + | TokenValue::RightBrace => break, + _ => self.parse_statement(parser, ctx, &mut body)?, + } + } + + let mut fall_through = true; + + for (i, stmt) in body.iter().enumerate() { + if let Statement::Break = *stmt { + fall_through = false; + body.drain(i..); + break; + } + } + + cases.push(SwitchCase { + value, + body, + fall_through, + }) + } + TokenValue::Default => { + let Token { meta, .. } = self.bump(parser)?; + self.expect(parser, TokenValue::Colon)?; + + if !default.is_empty() { + return Err(ErrorKind::SemanticError( + meta, + "Can only have one default case per switch statement".into(), + )); + } + + loop { + match self.expect_peek(parser)?.value { + TokenValue::Case | TokenValue::RightBrace => break, + _ => self.parse_statement(parser, ctx, &mut default)?, + } + } + + for (i, stmt) in default.iter().enumerate() { + if let Statement::Break = *stmt { + default.drain(i..); + break; + } + } + } + TokenValue::RightBrace => { + self.bump(parser)?; + break; + } + _ => { + return Err(ErrorKind::InvalidToken( + self.bump(parser)?, + vec![ + TokenValue::Case.into(), + TokenValue::Default.into(), + TokenValue::RightBrace.into(), + ], + )) + } + } + } + + body.push(Statement::Switch { + selector, + cases, + default, + }); + } + TokenValue::While => { + self.bump(parser)?; + + let mut loop_body = Block::new(); + + self.expect(parser, TokenValue::LeftParen)?; + let root = self.parse_expression(parser, ctx, &mut loop_body)?; + self.expect(parser, TokenValue::RightParen)?; + + let expr = ctx.lower_expect(parser, root, false, &mut loop_body)?.0; + let condition = ctx.add_expression( + Expression::Unary { + op: UnaryOperator::Not, + expr, + }, + &mut loop_body, + ); + + ctx.emit_flush(&mut loop_body); + ctx.emit_start(); + + loop_body.push(Statement::If { + condition, + accept: vec![Statement::Break], + reject: Block::new(), + }); + + self.parse_statement(parser, ctx, &mut loop_body)?; + + body.push(Statement::Loop { + body: loop_body, + continuing: Block::new(), + }) + } + TokenValue::Do => { + self.bump(parser)?; + + let mut loop_body = Block::new(); + self.parse_statement(parser, ctx, &mut loop_body)?; + + self.expect(parser, TokenValue::While)?; + self.expect(parser, TokenValue::LeftParen)?; + let root = self.parse_expression(parser, ctx, &mut loop_body)?; + self.expect(parser, TokenValue::RightParen)?; + + let expr = ctx.lower_expect(parser, root, false, &mut loop_body)?.0; + let condition = ctx.add_expression( + Expression::Unary { + op: UnaryOperator::Not, + expr, + }, + &mut loop_body, + ); + + ctx.emit_flush(&mut loop_body); + ctx.emit_start(); + + loop_body.push(Statement::If { + condition, + accept: vec![Statement::Break], + reject: Block::new(), + }); + + body.push(Statement::Loop { + body: loop_body, + continuing: Block::new(), + }) + } + TokenValue::For => { + self.bump(parser)?; + + ctx.push_scope(); + self.expect(parser, TokenValue::LeftParen)?; + + if self.bump_if(parser, TokenValue::Semicolon).is_none() { + if self.peek_type_name(parser) || self.peek_type_qualifier(parser) { + self.parse_declaration(parser, ctx, body, false)?; + } else { + let expr = self.parse_expression(parser, ctx, body)?; + ctx.lower(parser, expr, false, body)?; + self.expect(parser, TokenValue::Semicolon)?; + } + } + + let (mut block, mut continuing) = (Block::new(), Block::new()); + + if self.bump_if(parser, TokenValue::Semicolon).is_none() { + let expr = if self.peek_type_name(parser) || self.peek_type_qualifier(parser) { + let qualifiers = self.parse_type_qualifiers(parser)?; + let (ty, meta) = self.parse_type_non_void(parser)?; + let name = self.expect_ident(parser)?.0; + + self.expect(parser, TokenValue::Assign)?; + + let (value, end_meta) = + self.parse_initializer(parser, ty, ctx, &mut block)?; + + let decl = VarDeclaration { + qualifiers: &qualifiers, + ty, + name: Some(name), + init: None, + meta: meta.union(&end_meta), + }; + + let pointer = parser.add_local_var(ctx, &mut block, decl)?; + + ctx.emit_flush(&mut block); + ctx.emit_start(); + + block.push(Statement::Store { pointer, value }); + + value + } else { + let root = self.parse_expression(parser, ctx, &mut block)?; + ctx.lower_expect(parser, root, false, &mut block)?.0 + }; + + let condition = ctx.add_expression( + Expression::Unary { + op: UnaryOperator::Not, + expr, + }, + &mut block, + ); + + ctx.emit_flush(&mut block); + ctx.emit_start(); + + block.push(Statement::If { + condition, + accept: vec![Statement::Break], + reject: Block::new(), + }); + + self.expect(parser, TokenValue::Semicolon)?; + } + + match self.expect_peek(parser)?.value { + TokenValue::RightParen => {} + _ => { + let rest = self.parse_expression(parser, ctx, &mut continuing)?; + ctx.lower(parser, rest, false, &mut continuing)?; + } + } + + self.expect(parser, TokenValue::RightParen)?; + + self.parse_statement(parser, ctx, &mut block)?; + + body.push(Statement::Loop { + body: block, + continuing, + }); + + ctx.remove_current_scope(); + } + TokenValue::LeftBrace => { + self.bump(parser)?; + + let mut block = Block::new(); + ctx.push_scope(); + + self.parse_compound_statement(parser, ctx, &mut block)?; + + ctx.remove_current_scope(); + body.push(Statement::Block(block)); + } + TokenValue::Plus + | TokenValue::Dash + | TokenValue::Bang + | TokenValue::Tilde + | TokenValue::LeftParen + | TokenValue::Identifier(_) + | TokenValue::TypeName(_) + | TokenValue::IntConstant(_) + | TokenValue::BoolConstant(_) + | TokenValue::FloatConstant(_) => { + let expr = self.parse_expression(parser, ctx, body)?; + ctx.lower(parser, expr, false, body)?; + self.expect(parser, TokenValue::Semicolon)?; + } + TokenValue::Semicolon => { + self.bump(parser)?; + } + _ => {} + } + + Ok(()) + } + + pub fn parse_compound_statement( + &mut self, + parser: &mut Parser, + ctx: &mut Context, + body: &mut Block, + ) -> Result<()> { + loop { + if self.bump_if(parser, TokenValue::RightBrace).is_some() { + break; + } + + self.parse_statement(parser, ctx, body)?; + } + + Ok(()) + } + + pub fn parse_function_args( + &mut self, + parser: &mut Parser, + context: &mut Context, + body: &mut Block, + ) -> Result<()> { + loop { + if self.peek_type_name(parser) || self.peek_parameter_qualifier(parser) { + let qualifier = self.parse_parameter_qualifier(parser); + let ty = self.parse_type_non_void(parser)?.0; + + match self.expect_peek(parser)?.value { + TokenValue::Comma => { + self.bump(parser)?; + context.add_function_arg(parser, body, None, ty, qualifier); + continue; + } + TokenValue::Identifier(_) => { + let name = self.expect_ident(parser)?.0; + + let size = self.parse_array_specifier(parser)?; + let ty = parser.maybe_array(ty, size); + + context.add_function_arg(parser, body, Some(name), ty, qualifier); + + if self.bump_if(parser, TokenValue::Comma).is_some() { + continue; + } + + break; + } + _ => break, + } + } + + break; + } + + Ok(()) + } +} diff --git a/src/front/glsl/parser/types.rs b/src/front/glsl/parser/types.rs new file mode 100644 index 0000000000..9ee12a0f00 --- /dev/null +++ b/src/front/glsl/parser/types.rs @@ -0,0 +1,268 @@ +use crate::{ + front::glsl::{ + ast::{StorageQualifier, StructLayout, TypeQualifier}, + error::ExpectedToken, + parser::ParsingContext, + token::{SourceMetadata, TokenValue}, + ErrorKind, Parser, Result, + }, + ArraySize, Handle, ResourceBinding, StorageClass, Type, TypeInner, +}; + +impl<'source> ParsingContext<'source> { + /// Parses an optional array_specifier returning `Ok(None)` if there is no + /// LeftBracket + pub fn parse_array_specifier(&mut self, parser: &mut Parser) -> Result> { + if self.bump_if(parser, TokenValue::LeftBracket).is_some() { + if self.bump_if(parser, TokenValue::RightBracket).is_some() { + return Ok(Some(ArraySize::Dynamic)); + } + + let (constant, _) = self.parse_constant_expression(parser)?; + self.expect(parser, TokenValue::RightBracket)?; + Ok(Some(ArraySize::Constant(constant))) + } else { + Ok(None) + } + } + + pub fn parse_type( + &mut self, + parser: &mut Parser, + ) -> Result<(Option>, SourceMetadata)> { + let token = self.bump(parser)?; + let handle = match token.value { + TokenValue::Void => None, + TokenValue::TypeName(ty) => Some(parser.module.types.fetch_or_append(ty)), + TokenValue::Struct => { + let ty_name = self.expect_ident(parser)?.0; + self.expect(parser, TokenValue::LeftBrace)?; + let mut members = Vec::new(); + let span = + self.parse_struct_declaration_list(parser, &mut members, StructLayout::Std140)?; + self.expect(parser, TokenValue::RightBrace)?; + + let ty = parser.module.types.append(Type { + name: Some(ty_name.clone()), + inner: TypeInner::Struct { + top_level: false, + members, + span, + }, + }); + parser.lookup_type.insert(ty_name, ty); + Some(ty) + } + TokenValue::Identifier(ident) => match parser.lookup_type.get(&ident) { + Some(ty) => Some(*ty), + None => return Err(ErrorKind::UnknownType(token.meta, ident)), + }, + _ => { + return Err(ErrorKind::InvalidToken( + token, + vec![ + TokenValue::Void.into(), + TokenValue::Struct.into(), + ExpectedToken::TypeName, + ], + )) + } + }; + + let size = self.parse_array_specifier(parser)?; + Ok((handle.map(|ty| parser.maybe_array(ty, size)), token.meta)) + } + + pub fn parse_type_non_void( + &mut self, + parser: &mut Parser, + ) -> Result<(Handle, SourceMetadata)> { + let (maybe_ty, meta) = self.parse_type(parser)?; + let ty = + maybe_ty.ok_or_else(|| ErrorKind::SemanticError(meta, "Type can't be void".into()))?; + + Ok((ty, meta)) + } + + pub fn peek_type_qualifier(&mut self, parser: &mut Parser) -> bool { + self.peek(parser).map_or(false, |t| match t.value { + TokenValue::Interpolation(_) + | TokenValue::Sampling(_) + | TokenValue::PrecisionQualifier(_) + | TokenValue::Const + | TokenValue::In + | TokenValue::Out + | TokenValue::Uniform + | TokenValue::Buffer + | TokenValue::Restrict + | TokenValue::StorageAccess(_) + | TokenValue::Layout => true, + _ => false, + }) + } + + pub fn parse_type_qualifiers( + &mut self, + parser: &mut Parser, + ) -> Result> { + let mut qualifiers = Vec::new(); + + while self.peek_type_qualifier(parser) { + let token = self.bump(parser)?; + + // Handle layout qualifiers outside the match since this can push multiple values + if token.value == TokenValue::Layout { + self.parse_layout_qualifier_id_list(parser, &mut qualifiers)?; + continue; + } + + qualifiers.push(( + match token.value { + TokenValue::Interpolation(i) => TypeQualifier::Interpolation(i), + TokenValue::Const => TypeQualifier::StorageQualifier(StorageQualifier::Const), + TokenValue::In => TypeQualifier::StorageQualifier(StorageQualifier::Input), + TokenValue::Out => TypeQualifier::StorageQualifier(StorageQualifier::Output), + TokenValue::Uniform => TypeQualifier::StorageQualifier( + StorageQualifier::StorageClass(StorageClass::Uniform), + ), + TokenValue::Buffer => TypeQualifier::StorageQualifier( + StorageQualifier::StorageClass(StorageClass::Storage { + access: crate::StorageAccess::default(), + }), + ), + TokenValue::Sampling(s) => TypeQualifier::Sampling(s), + TokenValue::PrecisionQualifier(p) => TypeQualifier::Precision(p), + TokenValue::StorageAccess(access) => TypeQualifier::StorageAccess(access), + TokenValue::Restrict => continue, + _ => unreachable!(), + }, + token.meta, + )) + } + + Ok(qualifiers) + } + + pub fn parse_layout_qualifier_id_list( + &mut self, + parser: &mut Parser, + qualifiers: &mut Vec<(TypeQualifier, SourceMetadata)>, + ) -> Result<()> { + // We need both of these to produce a ResourceBinding + let mut group = None; + let mut binding = None; + + self.expect(parser, TokenValue::LeftParen)?; + loop { + self.parse_layout_qualifier_id(parser, qualifiers, &mut group, &mut binding)?; + + if self.bump_if(parser, TokenValue::Comma).is_some() { + continue; + } + + break; + } + self.expect(parser, TokenValue::RightParen)?; + + match (group, binding) { + (Some((group, group_meta)), Some((binding, binding_meta))) => qualifiers.push(( + TypeQualifier::ResourceBinding(ResourceBinding { group, binding }), + group_meta.union(&binding_meta), + )), + // Produce an error if we have one of group or binding but not the other + (Some((_, meta)), None) => { + return Err(ErrorKind::SemanticError( + meta, + "set specified with no binding".into(), + )) + } + (None, Some((_, meta))) => { + return Err(ErrorKind::SemanticError( + meta, + "binding specified with no set".into(), + )) + } + (None, None) => (), + } + + Ok(()) + } + + pub fn parse_layout_qualifier_id( + &mut self, + parser: &mut Parser, + qualifiers: &mut Vec<(TypeQualifier, SourceMetadata)>, + group: &mut Option<(u32, SourceMetadata)>, + binding: &mut Option<(u32, SourceMetadata)>, + ) -> Result<()> { + // layout_qualifier_id: + // IDENTIFIER + // IDENTIFIER EQUAL constant_expression + // SHARED + let mut token = self.bump(parser)?; + match token.value { + TokenValue::Identifier(name) => { + if self.bump_if(parser, TokenValue::Assign).is_some() { + let (value, end_meta) = self.parse_uint_constant(parser)?; + token.meta = token.meta.union(&end_meta); + + qualifiers.push(( + match name.as_str() { + "location" => TypeQualifier::Location(value), + "set" => { + *group = Some((value, end_meta)); + return Ok(()); + } + "binding" => { + *binding = Some((value, end_meta)); + return Ok(()); + } + "local_size_x" => TypeQualifier::WorkGroupSize(0, value), + "local_size_y" => TypeQualifier::WorkGroupSize(1, value), + "local_size_z" => TypeQualifier::WorkGroupSize(2, value), + _ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)), + }, + token.meta, + )) + } else { + match name.as_str() { + "push_constant" => { + qualifiers.push(( + TypeQualifier::StorageQualifier(StorageQualifier::StorageClass( + StorageClass::PushConstant, + )), + token.meta, + )); + qualifiers + .push((TypeQualifier::Layout(StructLayout::Std430), token.meta)); + } + "std140" => qualifiers + .push((TypeQualifier::Layout(StructLayout::Std140), token.meta)), + "std430" => qualifiers + .push((TypeQualifier::Layout(StructLayout::Std430), token.meta)), + "early_fragment_tests" => { + qualifiers.push((TypeQualifier::EarlyFragmentTests, token.meta)) + } + _ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)), + } + }; + + Ok(()) + } + // TODO: handle Shared? + _ => Err(ErrorKind::InvalidToken( + token, + vec![ExpectedToken::Identifier], + )), + } + } + + pub fn peek_type_name(&mut self, parser: &mut Parser) -> bool { + self.peek(parser).map_or(false, |t| match t.value { + TokenValue::TypeName(_) | TokenValue::Void => true, + TokenValue::Struct => true, + TokenValue::Identifier(ref ident) => parser.lookup_type.contains_key(ident), + _ => false, + }) + } +} diff --git a/src/front/glsl/parser_tests.rs b/src/front/glsl/parser_tests.rs index 72372c9879..aec3824805 100644 --- a/src/front/glsl/parser_tests.rs +++ b/src/front/glsl/parser_tests.rs @@ -1,53 +1,55 @@ use pp_rs::token::PreprocessorError; -use super::lex::Lexer; -use super::parser; -use super::{ast::Profile, error::ErrorKind}; -use super::{ast::Program, SourceMetadata}; -use crate::front::glsl::error::ExpectedToken; use crate::{ - front::glsl::{token::TokenValue, Token}, - ShaderStage, + front::glsl::{ + ast::Profile, error::ErrorKind, error::ExpectedToken, token::TokenValue, Options, Parser, + SourceMetadata, Token, + }, + Module, ShaderStage, }; -fn parse_program(source: &str, stage: ShaderStage) -> Result { - let mut program = Program::new(stage); +fn parse(parser: &mut Parser, source: &str, stage: ShaderStage) -> Result { let defines = crate::FastHashMap::default(); - let lex = Lexer::new(source, &defines); - let mut parser = parser::Parser::new(&mut program, lex); - parser.parse()?; - Ok(program) + parser + .parse(&Options { stage, defines }, source) + .map_err(|e| e.kind) } #[test] fn version() { + let mut parser = Parser::default(); + // invalid versions assert_eq!( - parse_program("#version 99000", ShaderStage::Vertex) + parse(&mut parser, "#version 99000", ShaderStage::Vertex) .err() .unwrap(), ErrorKind::InvalidVersion(SourceMetadata { start: 9, end: 14 }, 99000), ); assert_eq!( - parse_program("#version 449", ShaderStage::Vertex) + parse(&mut parser, "#version 449", ShaderStage::Vertex) .err() .unwrap(), ErrorKind::InvalidVersion(SourceMetadata { start: 9, end: 12 }, 449) ); assert_eq!( - parse_program("#version 450 smart", ShaderStage::Vertex) + parse(&mut parser, "#version 450 smart", ShaderStage::Vertex) .err() .unwrap(), ErrorKind::InvalidProfile(SourceMetadata { start: 13, end: 18 }, "smart".into()) ); assert_eq!( - parse_program("#version 450\nvoid f(){} #version 450", ShaderStage::Vertex) - .err() - .unwrap(), + parse( + &mut parser, + "#version 450\nvoid f(){} #version 450", + ShaderStage::Vertex + ) + .err() + .unwrap(), ErrorKind::InvalidToken( Token { value: TokenValue::Unknown(PreprocessorError::UnexpectedHash), @@ -58,19 +60,46 @@ fn version() { ); // valid versions - let program = parse_program(" # version 450\nvoid main() {}", ShaderStage::Vertex).unwrap(); - assert_eq!((program.version, program.profile), (450, Profile::Core)); + parse( + &mut parser, + " # version 450\nvoid main() {}", + ShaderStage::Vertex, + ) + .unwrap(); + assert_eq!( + (parser.metadata().version, parser.metadata().profile), + (450, Profile::Core) + ); - let program = parse_program("#version 450\nvoid main() {}", ShaderStage::Vertex).unwrap(); - assert_eq!((program.version, program.profile), (450, Profile::Core)); + parse( + &mut parser, + "#version 450\nvoid main() {}", + ShaderStage::Vertex, + ) + .unwrap(); + assert_eq!( + (parser.metadata().version, parser.metadata().profile), + (450, Profile::Core) + ); - let program = parse_program("#version 450 core\nvoid main() {}", ShaderStage::Vertex).unwrap(); - assert_eq!((program.version, program.profile), (450, Profile::Core)); + parse( + &mut parser, + "#version 450 core\nvoid main() {}", + ShaderStage::Vertex, + ) + .unwrap(); + assert_eq!( + (parser.metadata().version, parser.metadata().profile), + (450, Profile::Core) + ); } #[test] fn control_flow() { - let _program = parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" # version 450 void main() { @@ -85,7 +114,8 @@ fn control_flow() { ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" # version 450 void main() { @@ -98,7 +128,8 @@ fn control_flow() { ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" # version 450 void main() { @@ -119,7 +150,8 @@ fn control_flow() { ShaderStage::Vertex, ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" # version 450 void main() { @@ -136,7 +168,8 @@ fn control_flow() { ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" # version 450 void main() { @@ -155,7 +188,10 @@ fn control_flow() { #[test] fn declarations() { - let _program = parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" #version 450 layout(location = 0) in vec2 v_uv; @@ -164,12 +200,15 @@ fn declarations() { layout(set = 1, binding = 2) uniform sampler tex_sampler; layout(early_fragment_tests) in; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" #version 450 layout(std140, set = 2, binding = 0) @@ -178,12 +217,15 @@ fn declarations() { float load_time; ivec4 atlas_offs; }; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" #version 450 layout(push_constant, set = 2, binding = 0) @@ -192,12 +234,15 @@ fn declarations() { float load_time; ivec4 atlas_offs; }; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" #version 450 layout(std430, set = 2, binding = 0) @@ -206,12 +251,15 @@ fn declarations() { float load_time; ivec4 atlas_offs; }; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" #version 450 layout(std140, set = 2, binding = 0) @@ -229,19 +277,25 @@ fn declarations() { ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" #version 450 float vector = vec4(1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0); + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - let _program = parse_program( + parse( + &mut parser, r#" #version 450 precision highp float; + + void main() {} "#, ShaderStage::Vertex, ) @@ -250,7 +304,10 @@ fn declarations() { #[test] fn textures() { - let _program = parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" #version 450 layout(location = 0) in vec2 v_uv; @@ -269,7 +326,10 @@ fn textures() { #[test] fn functions() { - parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" # version 450 void test1(float); @@ -281,7 +341,8 @@ fn functions() { ) .unwrap(); - parse_program( + parse( + &mut parser, r#" # version 450 void test2(float a) {} @@ -294,7 +355,8 @@ fn functions() { ) .unwrap(); - parse_program( + parse( + &mut parser, r#" # version 450 float test(float a) { return a; } @@ -305,19 +367,23 @@ fn functions() { ) .unwrap(); - parse_program( + parse( + &mut parser, r#" # version 450 float test(vec4 p) { return p.x; } + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); // Function overloading - parse_program( + parse( + &mut parser, r#" # version 450 float test(vec2 p); @@ -335,13 +401,16 @@ fn functions() { float test(vec4 p) { return p.x; } + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); assert_eq!( - parse_program( + parse( + &mut parser, r#" # version 450 int test(vec4 p) { @@ -351,6 +420,8 @@ fn functions() { float test(vec4 p) { return p.x; } + + void main() {} "#, ShaderStage::Vertex ) @@ -367,7 +438,8 @@ fn functions() { println!(); - let _program = parse_program( + parse( + &mut parser, r#" # version 450 float callee(uint q) { @@ -377,13 +449,16 @@ fn functions() { float caller() { callee(1u); } + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); // Nested function call - let _program = parse_program( + parse( + &mut parser, r#" # version 450 layout(set = 0, binding = 1) uniform texture2D t_noise; @@ -397,7 +472,8 @@ fn functions() { ) .unwrap(); - parse_program( + parse( + &mut parser, r#" # version 450 void fun(vec2 in_parameter, out float out_parameter) { @@ -417,19 +493,23 @@ fn functions() { #[test] fn constants() { use crate::{Constant, ConstantInner, ScalarValue}; + let mut parser = Parser::default(); - let program = parse_program( + let module = parse( + &mut parser, r#" # version 450 const float a = 1.0; float global = a; const float b = a; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - let mut constants = program.module.constants.iter(); + let mut constants = module.constants.iter(); assert_eq!( constants.next().unwrap().1, @@ -448,7 +528,10 @@ fn constants() { #[test] fn function_overloading() { - parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" # version 450 @@ -471,7 +554,10 @@ fn function_overloading() { #[test] fn implicit_conversions() { - parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" # version 450 void main() { @@ -485,7 +571,8 @@ fn implicit_conversions() { .unwrap(); assert_eq!( - parse_program( + parse( + &mut parser, r#" # version 450 void test(int a) {} @@ -509,7 +596,8 @@ fn implicit_conversions() { ); assert_eq!( - parse_program( + parse( + &mut parser, r#" # version 450 void test(float a) {} @@ -535,41 +623,53 @@ fn implicit_conversions() { #[test] fn structs() { - parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" # version 450 Test { vec4 pos; } xx; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap_err(); - parse_program( + parse( + &mut parser, r#" # version 450 struct Test { vec4 pos; }; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - parse_program( + parse( + &mut parser, r#" # version 450 const int NUM_VECS = 42; struct Test { vec4 vecs[NUM_VECS]; }; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - parse_program( + parse( + &mut parser, r#" # version 450 struct Hello { @@ -577,26 +677,34 @@ fn structs() { } test() { return Hello( vec4(1.0) ); } + + void main() {} "#, ShaderStage::Vertex, ) .unwrap(); - parse_program( + parse( + &mut parser, r#" # version 450 struct Test {}; + + void main() {} "#, ShaderStage::Vertex, ) .unwrap_err(); - parse_program( + parse( + &mut parser, r#" # version 450 inout struct Test { vec4 x; }; + + void main() {} "#, ShaderStage::Vertex, ) @@ -605,7 +713,10 @@ fn structs() { #[test] fn swizzles() { - parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" # version 450 void main() { @@ -619,7 +730,8 @@ fn swizzles() { ) .unwrap(); - parse_program( + parse( + &mut parser, r#" # version 450 void main() { @@ -631,7 +743,8 @@ fn swizzles() { ) .unwrap_err(); - parse_program( + parse( + &mut parser, r#" # version 450 void main() { @@ -646,13 +759,18 @@ fn swizzles() { #[test] fn vector_indexing() { - parse_program( + let mut parser = Parser::default(); + + parse( + &mut parser, r#" # version 450 - float main(int index) { + float test(int index) { vec4 v = vec4(1.0, 2.0, 3.0, 4.0); return v[index] + 1.0; } + + void main() {} "#, ShaderStage::Vertex, ) diff --git a/src/front/glsl/types.rs b/src/front/glsl/types.rs index 73812276a5..cac9137929 100644 --- a/src/front/glsl/types.rs +++ b/src/front/glsl/types.rs @@ -1,4 +1,11 @@ -use crate::{ImageClass, ImageDimension, ScalarKind, Type, TypeInner, VectorSize}; +use crate::{ + front::glsl::{ + constants::ConstantSolver, context::Context, ErrorKind, Parser, Result, SourceMetadata, + }, + proc::ResolveContext, + ArraySize, Constant, Expression, Handle, ImageClass, ImageDimension, ScalarKind, Type, + TypeInner, VectorSize, +}; pub fn parse_type(type_name: &str) -> Option { match type_name { @@ -152,3 +159,108 @@ pub fn parse_type(type_name: &str) -> Option { } } } + +pub fn scalar_components(ty: &TypeInner) -> Option<(ScalarKind, crate::Bytes)> { + match *ty { + TypeInner::Scalar { kind, width } => Some((kind, width)), + TypeInner::Vector { kind, width, .. } => Some((kind, width)), + TypeInner::Matrix { width, .. } => Some((ScalarKind::Float, width)), + TypeInner::ValuePointer { kind, width, .. } => Some((kind, width)), + _ => None, + } +} + +pub fn type_power(kind: ScalarKind) -> Option { + Some(match kind { + ScalarKind::Sint => 0, + ScalarKind::Uint => 1, + ScalarKind::Float => 2, + ScalarKind::Bool => return None, + }) +} + +impl Parser { + pub fn typifier_grow( + &self, + ctx: &mut Context, + handle: Handle, + meta: SourceMetadata, + ) -> Result<()> { + let resolve_ctx = ResolveContext { + constants: &self.module.constants, + types: &self.module.types, + global_vars: &self.module.global_variables, + local_vars: &ctx.locals, + functions: &self.module.functions, + arguments: &ctx.arguments, + }; + + ctx.typifier + .grow(handle, &ctx.expressions, &resolve_ctx) + .map_err(|error| { + ErrorKind::SemanticError(meta, format!("Can't resolve type: {:?}", error).into()) + }) + } + + pub fn resolve_type<'b>( + &'b self, + ctx: &'b mut Context, + handle: Handle, + meta: SourceMetadata, + ) -> Result<&'b TypeInner> { + self.typifier_grow(ctx, handle, meta)?; + Ok(ctx.typifier.get(handle, &self.module.types)) + } + + /// Invalidates the cached type resolution for `handle` forcing a recomputation + pub fn invalidate_expression<'b>( + &'b self, + ctx: &'b mut Context, + handle: Handle, + meta: SourceMetadata, + ) -> Result<()> { + let resolve_ctx = ResolveContext { + constants: &self.module.constants, + types: &self.module.types, + global_vars: &self.module.global_variables, + local_vars: &ctx.locals, + functions: &self.module.functions, + arguments: &ctx.arguments, + }; + + ctx.typifier + .invalidate(handle, &ctx.expressions, &resolve_ctx) + .map_err(|error| { + ErrorKind::SemanticError(meta, format!("Can't resolve type: {:?}", error).into()) + }) + } + + pub fn solve_constant( + &mut self, + ctx: &Context, + root: Handle, + meta: SourceMetadata, + ) -> Result> { + let mut solver = ConstantSolver { + types: &self.module.types, + expressions: &ctx.expressions, + constants: &mut self.module.constants, + }; + + solver.solve(root).map_err(|e| (meta, e).into()) + } + + pub fn maybe_array(&mut self, base: Handle, size: Option) -> Handle { + size.map(|size| { + self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Array { + base, + size, + stride: self.module.types[base].inner.span(&self.module.constants), + }, + }) + }) + .unwrap_or(base) + } +} diff --git a/src/front/glsl/variables.rs b/src/front/glsl/variables.rs index 13182ca530..3057cbd7d0 100644 --- a/src/front/glsl/variables.rs +++ b/src/front/glsl/variables.rs @@ -1,13 +1,10 @@ use crate::{ + front::glsl::{ast::*, context::Context, error::ErrorKind, Parser, SourceMetadata}, Binding, Block, BuiltIn, Constant, Expression, GlobalVariable, Handle, Interpolation, LocalVariable, ScalarKind, StorageAccess, StorageClass, SwizzleComponent, Type, TypeInner, VectorSize, }; -use super::ast::*; -use super::error::ErrorKind; -use super::token::SourceMetadata; - macro_rules! qualifier_arm { ($src:expr, $tgt:expr, $meta:expr, $msg:literal $(,)?) => {{ if $tgt.is_some() { @@ -31,7 +28,7 @@ pub enum GlobalOrConstant { Constant(Handle), } -impl Program { +impl Parser { pub fn lookup_variable( &mut self, ctx: &mut Context, @@ -453,7 +450,7 @@ impl Program { entry_arg: Some(idx), mutable: !input, }; - ctx.add_global(&name, lookup, self, body); + ctx.add_global(self, &name, lookup, body); self.global_variables.push((name, lookup)); } @@ -469,7 +466,7 @@ impl Program { entry_arg: None, mutable: false, }; - ctx.add_global(&name, lookup, self, body); + ctx.add_global(self, &name, lookup, body); self.global_variables.push((name, lookup)); } @@ -505,7 +502,7 @@ impl Program { entry_arg: None, mutable: true, }; - ctx.add_global(&name, lookup, self, body); + ctx.add_global(self, &name, lookup, body); self.global_variables.push((name, lookup)); } diff --git a/src/front/mod.rs b/src/front/mod.rs index 8f620f4309..4bd1294cea 100644 --- a/src/front/mod.rs +++ b/src/front/mod.rs @@ -50,7 +50,7 @@ impl super::ConstantInner { } /// Helper processor that derives the types of all expressions. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Typifier { resolutions: Vec, } @@ -62,6 +62,10 @@ impl Typifier { } } + pub fn reset(&mut self) { + self.resolutions.clear() + } + pub fn get<'a>( &'a self, expr_handle: Handle, diff --git a/tests/in/glsl/931-constant-emitting.vert b/tests/in/glsl/931-constant-emitting.vert index b780eb7ac9..1a3300bc81 100644 --- a/tests/in/glsl/931-constant-emitting.vert +++ b/tests/in/glsl/931-constant-emitting.vert @@ -8,3 +8,5 @@ const int constant = 10; float function() { return 0.0; } + +void main() {} diff --git a/tests/in/glsl/constant-array-size.vert b/tests/in/glsl/constant-array-size.vert index f1c7a500e9..9d0f580b51 100644 --- a/tests/in/glsl/constant-array-size.vert +++ b/tests/in/glsl/constant-array-size.vert @@ -12,3 +12,5 @@ vec4 function() { } return sum; } + +void main() {} diff --git a/tests/out/wgsl/931-constant-emitting-vert.wgsl b/tests/out/wgsl/931-constant-emitting-vert.wgsl index 43ccc88002..db5670f1a0 100644 --- a/tests/out/wgsl/931-constant-emitting-vert.wgsl +++ b/tests/out/wgsl/931-constant-emitting-vert.wgsl @@ -2,3 +2,12 @@ fn function() -> f32 { return 0.0; } +fn main1() { + return; +} + +[[stage(vertex)]] +fn main() { + main1(); + return; +} diff --git a/tests/out/wgsl/constant-array-size-vert.wgsl b/tests/out/wgsl/constant-array-size-vert.wgsl index 5a7aba0abc..2eaec30862 100644 --- a/tests/out/wgsl/constant-array-size-vert.wgsl +++ b/tests/out/wgsl/constant-array-size-vert.wgsl @@ -32,3 +32,12 @@ fn function() -> vec4 { return _e22; } +fn main1() { + return; +} + +[[stage(vertex)]] +fn main() { + main1(); + return; +} diff --git a/tests/snapshots.rs b/tests/snapshots.rs index 8b4729968c..191ef8b622 100644 --- a/tests/snapshots.rs +++ b/tests/snapshots.rs @@ -523,19 +523,21 @@ fn convert_glsl_folder() { } println!("Processing {}", file_name); - let module = naga::front::glsl::parse_str( - &fs::read_to_string(entry.path()).expect("Couldn't find glsl file"), - &naga::front::glsl::Options { - stage: match entry.path().extension().and_then(|s| s.to_str()).unwrap() { - "vert" => naga::ShaderStage::Vertex, - "frag" => naga::ShaderStage::Fragment, - "comp" => naga::ShaderStage::Compute, - ext => panic!("Unknown extension for glsl file {}", ext), + let mut parser = naga::front::glsl::Parser::default(); + let module = parser + .parse( + &naga::front::glsl::Options { + stage: match entry.path().extension().and_then(|s| s.to_str()).unwrap() { + "vert" => naga::ShaderStage::Vertex, + "frag" => naga::ShaderStage::Fragment, + "comp" => naga::ShaderStage::Compute, + ext => panic!("Unknown extension for glsl file {}", ext), + }, + defines: Default::default(), }, - defines: Default::default(), - }, - ) - .unwrap(); + &fs::read_to_string(entry.path()).expect("Couldn't find glsl file"), + ) + .unwrap(); let info = naga::valid::Validator::new( naga::valid::ValidationFlags::all(),