mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-27 09:14:01 +00:00
[glsl-in] API revamp
The new api allows for reuse while keeping some allocations and to please the borrow checker in future work, it also splits the parser into logical modules to make it easier to work on.
This commit is contained in:
parent
0bd78ef85e
commit
a8a316da15
@ -221,50 +221,28 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
"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()),
|
||||
};
|
||||
|
@ -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<String, Vec<FunctionDeclaration>>,
|
||||
pub lookup_type: FastHashMap<String, Handle<Type>>,
|
||||
|
||||
pub global_variables: Vec<(String, GlobalLookup)>,
|
||||
|
||||
pub entry_args: Vec<EntryArg>,
|
||||
pub entry_point: Option<Handle<Function>>,
|
||||
|
||||
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<Expression>,
|
||||
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<Expression>,
|
||||
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<Expression>,
|
||||
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<Expression>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<Handle<Constant>, 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<Expression>,
|
||||
pub locals: &'function mut Arena<LocalVariable>,
|
||||
pub arguments: &'function mut Vec<FunctionArgument>,
|
||||
pub parameters_info: Vec<ParameterInfo>,
|
||||
|
||||
//TODO: Find less allocation heavy representation
|
||||
pub scopes: Vec<FastHashMap<String, VariableReference>>,
|
||||
pub lookup_global_var_exps: FastHashMap<String, VariableReference>,
|
||||
pub samplers: FastHashMap<Handle<Expression>, Handle<Expression>>,
|
||||
pub typifier: Typifier,
|
||||
|
||||
pub hir_exprs: Arena<HirExpr>,
|
||||
emitter: Emitter,
|
||||
}
|
||||
|
||||
impl<'function> Context<'function> {
|
||||
pub fn new(
|
||||
program: &mut Program,
|
||||
body: &mut Block,
|
||||
expressions: &'function mut Arena<Expression>,
|
||||
locals: &'function mut Arena<LocalVariable>,
|
||||
arguments: &'function mut Vec<FunctionArgument>,
|
||||
) -> 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<Expression> {
|
||||
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<VariableReference> {
|
||||
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<VariableReference> {
|
||||
self.lookup_global_var_exps.get(name).cloned()
|
||||
}
|
||||
|
||||
#[cfg(feature = "glsl-validate")]
|
||||
pub fn lookup_local_var_current_scope(&self, name: &str) -> Option<VariableReference> {
|
||||
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<Expression>, 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<Handle<Type>>,
|
||||
body: &mut Block,
|
||||
name: Option<String>,
|
||||
ty: Handle<Type>,
|
||||
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<HirExpr>,
|
||||
lhs: bool,
|
||||
body: &mut Block,
|
||||
) -> Result<(Handle<Expression>, 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<HirExpr>,
|
||||
lhs: bool,
|
||||
body: &mut Block,
|
||||
) -> Result<(Option<Handle<Expression>>, 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<Expression>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<Option<(ScalarKind, crate::Bytes)>, ErrorKind> {
|
||||
let ty = program.resolve_type(self, expr, meta)?;
|
||||
Ok(scalar_components(ty))
|
||||
}
|
||||
|
||||
pub fn expr_power(
|
||||
&mut self,
|
||||
program: &mut Program,
|
||||
expr: Handle<Expression>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<Option<u32>, ErrorKind> {
|
||||
Ok(self
|
||||
.expr_scalar_components(program, expr, meta)?
|
||||
.and_then(|(kind, _)| type_power(kind)))
|
||||
}
|
||||
|
||||
pub fn get_expression(&self, expr: Handle<Expression>) -> &Expression {
|
||||
&self.expressions[expr]
|
||||
}
|
||||
|
||||
pub fn implicit_conversion(
|
||||
&mut self,
|
||||
program: &mut Program,
|
||||
expr: &mut Handle<Expression>,
|
||||
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<Expression>,
|
||||
left_meta: SourceMetadata,
|
||||
right: &mut Handle<Expression>,
|
||||
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<Expression>,
|
||||
meta: SourceMetadata,
|
||||
vector_size: Option<VectorSize>,
|
||||
) -> 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<Expression>,
|
||||
body: &mut Block,
|
||||
) -> Handle<Expression> {
|
||||
self.add_expression(
|
||||
Expression::Swizzle {
|
||||
size,
|
||||
vector,
|
||||
pattern: crate::SwizzleComponent::XYZW,
|
||||
},
|
||||
body,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'function> Index<Handle<Expression>> for Context<'function> {
|
||||
type Output = Expression;
|
||||
|
||||
fn index(&self, index: Handle<Expression>) -> &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<u32> {
|
||||
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<Expression>,
|
||||
@ -1087,3 +163,8 @@ impl ParameterQualifier {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Profile {
|
||||
Core,
|
||||
}
|
||||
|
787
src/front/glsl/context.rs
Normal file
787
src/front/glsl/context.rs
Normal file
@ -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<Expression>,
|
||||
pub locals: Arena<LocalVariable>,
|
||||
pub arguments: Vec<FunctionArgument>,
|
||||
|
||||
pub parameters: Vec<Handle<Type>>,
|
||||
pub parameters_info: Vec<ParameterInfo>,
|
||||
|
||||
//TODO: Find less allocation heavy representation
|
||||
pub scopes: Vec<FastHashMap<String, VariableReference>>,
|
||||
pub lookup_global_var_exps: FastHashMap<String, VariableReference>,
|
||||
pub samplers: FastHashMap<Handle<Expression>, Handle<Expression>>,
|
||||
|
||||
pub hir_exprs: Arena<HirExpr>,
|
||||
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<Expression> {
|
||||
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<VariableReference> {
|
||||
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<VariableReference> {
|
||||
self.lookup_global_var_exps.get(name).cloned()
|
||||
}
|
||||
|
||||
#[cfg(feature = "glsl-validate")]
|
||||
pub fn lookup_local_var_current_scope(&self, name: &str) -> Option<VariableReference> {
|
||||
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<Expression>, 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<String>,
|
||||
ty: Handle<Type>,
|
||||
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<HirExpr>,
|
||||
lhs: bool,
|
||||
body: &mut Block,
|
||||
) -> Result<(Handle<Expression>, 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<HirExpr>,
|
||||
lhs: bool,
|
||||
body: &mut Block,
|
||||
) -> Result<(Option<Handle<Expression>>, 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<Expression>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<Option<(ScalarKind, crate::Bytes)>, ErrorKind> {
|
||||
let ty = parser.resolve_type(self, expr, meta)?;
|
||||
Ok(scalar_components(ty))
|
||||
}
|
||||
|
||||
pub fn expr_power(
|
||||
&mut self,
|
||||
parser: &mut Parser,
|
||||
expr: Handle<Expression>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<Option<u32>, ErrorKind> {
|
||||
Ok(self
|
||||
.expr_scalar_components(parser, expr, meta)?
|
||||
.and_then(|(kind, _)| type_power(kind)))
|
||||
}
|
||||
|
||||
pub fn get_expression(&self, expr: Handle<Expression>) -> &Expression {
|
||||
&self.expressions[expr]
|
||||
}
|
||||
|
||||
pub fn implicit_conversion(
|
||||
&mut self,
|
||||
parser: &mut Parser,
|
||||
expr: &mut Handle<Expression>,
|
||||
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<Expression>,
|
||||
left_meta: SourceMetadata,
|
||||
right: &mut Handle<Expression>,
|
||||
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<Expression>,
|
||||
meta: SourceMetadata,
|
||||
vector_size: Option<VectorSize>,
|
||||
) -> 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<Expression>,
|
||||
body: &mut Block,
|
||||
) -> Handle<Expression> {
|
||||
self.add_expression(
|
||||
Expression::Swizzle {
|
||||
size,
|
||||
vector,
|
||||
pattern: crate::SwizzleComponent::XYZW,
|
||||
},
|
||||
body,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Handle<Expression>> for Context {
|
||||
type Output = Expression;
|
||||
|
||||
fn index(&self, index: Handle<Expression>) -> &Self::Output {
|
||||
&self.expressions[index]
|
||||
}
|
||||
}
|
@ -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<Expression>,
|
||||
depth_ref: Option<Handle<Expression>>,
|
||||
array_index: Option<Handle<Expression>>,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
impl Parser {
|
||||
fn add_constant_value(&mut self, scalar_kind: ScalarKind, value: u64) -> Handle<Constant> {
|
||||
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<Handle<Type>>,
|
||||
parameters_info: Vec<ParameterInfo>,
|
||||
result: Option<FunctionResult>,
|
||||
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<Handle<Type>>,
|
||||
parameters_info: Vec<ParameterInfo>,
|
||||
result: Option<FunctionResult>,
|
||||
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<Expression>, SourceMetadata),
|
||||
(coord, coord_meta): (Handle<Expression>, SourceMetadata),
|
||||
|
@ -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<T> = std::result::Result<T, ErrorKind>;
|
||||
|
||||
pub struct Options {
|
||||
pub stage: ShaderStage,
|
||||
pub defines: FastHashMap<String, String>,
|
||||
}
|
||||
|
||||
pub fn parse_str(source: &str, options: &Options) -> Result<Module, ParseError> {
|
||||
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<String, String>,
|
||||
}
|
||||
|
||||
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<String, Vec<FunctionDeclaration>>,
|
||||
lookup_type: FastHashMap<String, Handle<Type>>,
|
||||
|
||||
global_variables: Vec<(String, GlobalLookup)>,
|
||||
|
||||
entry_args: Vec<EntryArg>,
|
||||
|
||||
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<Module, ParseError> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
581
src/front/glsl/parser/declarations.rs
Normal file
581
src/front/glsl/parser/declarations.rs
Normal file
@ -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<Type>,
|
||||
ctx: &mut Context,
|
||||
body: &mut Block,
|
||||
) -> Result<(Handle<Expression>, 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<Type>,
|
||||
mut fallthrough: Option<Token>,
|
||||
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::<Result<_>, _>(|_| {
|
||||
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<bool> {
|
||||
//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<bool> {
|
||||
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<StructMember>,
|
||||
layout: StructLayout,
|
||||
) -> Result<u32> {
|
||||
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)
|
||||
}
|
||||
}
|
486
src/front/glsl/parser/expressions.rs
Normal file
486
src/front/glsl/parser/expressions.rs
Normal file
@ -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<Handle<HirExpr>> {
|
||||
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<Vec<Handle<HirExpr>>> {
|
||||
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<Handle<HirExpr>> {
|
||||
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<Handle<HirExpr>> {
|
||||
// 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<Handle<HirExpr>>,
|
||||
min_bp: u8,
|
||||
) -> Result<Handle<HirExpr>> {
|
||||
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<Handle<HirExpr>>,
|
||||
) -> Result<Handle<HirExpr>> {
|
||||
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<Handle<HirExpr>> {
|
||||
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<Handle<HirExpr>> {
|
||||
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,
|
||||
})
|
||||
}
|
483
src/front/glsl/parser/functions.rs
Normal file
483
src/front/glsl/parser/functions.rs
Normal file
@ -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(())
|
||||
}
|
||||
}
|
268
src/front/glsl/parser/types.rs
Normal file
268
src/front/glsl/parser/types.rs
Normal file
@ -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<Option<ArraySize>> {
|
||||
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<Handle<Type>>, 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<Type>, 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<Vec<(TypeQualifier, SourceMetadata)>> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
@ -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<Program, ErrorKind> {
|
||||
let mut program = Program::new(stage);
|
||||
fn parse(parser: &mut Parser, source: &str, stage: ShaderStage) -> Result<Module, ErrorKind> {
|
||||
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,
|
||||
)
|
||||
|
@ -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<Type> {
|
||||
match type_name {
|
||||
@ -152,3 +159,108 @@ pub fn parse_type(type_name: &str) -> Option<Type> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<u32> {
|
||||
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<Expression>,
|
||||
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<Expression>,
|
||||
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<Expression>,
|
||||
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<Expression>,
|
||||
meta: SourceMetadata,
|
||||
) -> Result<Handle<Constant>> {
|
||||
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<Type>, size: Option<ArraySize>) -> Handle<Type> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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<Constant>),
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
@ -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<TypeResolution>,
|
||||
}
|
||||
@ -62,6 +62,10 @@ impl Typifier {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.resolutions.clear()
|
||||
}
|
||||
|
||||
pub fn get<'a>(
|
||||
&'a self,
|
||||
expr_handle: Handle<crate::Expression>,
|
||||
|
@ -8,3 +8,5 @@ const int constant = 10;
|
||||
float function() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void main() {}
|
||||
|
@ -12,3 +12,5 @@ vec4 function() {
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
void main() {}
|
||||
|
@ -2,3 +2,12 @@ fn function() -> f32 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
fn main1() {
|
||||
return;
|
||||
}
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn main() {
|
||||
main1();
|
||||
return;
|
||||
}
|
||||
|
@ -32,3 +32,12 @@ fn function() -> vec4<f32> {
|
||||
return _e22;
|
||||
}
|
||||
|
||||
fn main1() {
|
||||
return;
|
||||
}
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn main() {
|
||||
main1();
|
||||
return;
|
||||
}
|
||||
|
@ -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(),
|
||||
|
Loading…
Reference in New Issue
Block a user