Files
wgpu/src/front/glsl/parser.rs
2021-06-01 21:12:46 -04:00

1685 lines
61 KiB
Rust

use super::{
ast::{
Context, FunctionCall, FunctionCallKind, FunctionSignature, GlobalLookup, GlobalLookupKind,
HirExpr, HirExprKind, ParameterQualifier, Profile, StorageQualifier, StructLayout,
TypeQualifier,
},
error::ErrorKind,
lex::Lexer,
token::{SourceMetadata, Token, TokenValue},
variables::VarDeclaration,
Program,
};
use crate::{
arena::Handle, Arena, ArraySize, BinaryOperator, Block, Constant, ConstantInner, Expression,
Function, FunctionResult, GlobalVariable, ResourceBinding, ScalarValue, Statement,
StorageAccess, StorageClass, StructMember, SwitchCase, Type, TypeInner, UnaryOperator,
};
use core::convert::TryFrom;
use std::{iter::Peekable, mem};
type Result<T> = std::result::Result<T, ErrorKind>;
pub struct Parser<'source, 'program, 'options> {
program: &'program mut Program<'options>,
lexer: Peekable<Lexer<'source>>,
}
impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
pub fn new(program: &'program mut Program<'options>, lexer: Lexer<'source>) -> Self {
Parser {
program,
lexer: lexer.peekable(),
}
}
fn expect_ident(&mut self) -> Result<(String, SourceMetadata)> {
let token = self.bump()?;
match token.value {
TokenValue::Identifier(name) => Ok((name, token.meta)),
_ => Err(ErrorKind::InvalidToken(token)),
}
}
fn expect(&mut self, value: TokenValue) -> Result<Token> {
let token = self.bump()?;
if token.value != value {
Err(ErrorKind::InvalidToken(token))
} else {
Ok(token)
}
}
fn bump(&mut self) -> Result<Token> {
self.lexer.next().ok_or(ErrorKind::EndOfFile)
}
/// Returns None on the end of the file rather than an error like other methods
fn bump_if(&mut self, value: TokenValue) -> Option<Token> {
if self.lexer.peek().filter(|t| t.value == value).is_some() {
self.bump().ok()
} else {
None
}
}
fn expect_peek(&mut self) -> Result<&Token> {
self.lexer.peek().ok_or(ErrorKind::EndOfFile)
}
pub fn parse(&mut self) -> Result<()> {
self.parse_version()?;
while self.lexer.peek().is_some() {
self.parse_external_declaration()?;
}
self.program.add_entry_points();
Ok(())
}
fn parse_version(&mut self) -> Result<()> {
self.expect(TokenValue::Version)?;
let version = self.bump()?;
match version.value {
TokenValue::IntConstant(i) => match i.value {
440 | 450 | 460 => self.program.version = i.value as u16,
_ => return Err(ErrorKind::InvalidVersion(version.meta, i.value)),
},
_ => return Err(ErrorKind::InvalidToken(version)),
}
let profile = self.lexer.peek();
self.program.profile = match profile {
Some(&Token {
value: TokenValue::Identifier(_),
..
}) => {
let (name, meta) = self.expect_ident()?;
match name.as_str() {
"core" => Profile::Core,
_ => return Err(ErrorKind::InvalidProfile(meta, name)),
}
}
_ => Profile::Core,
};
Ok(())
}
/// Parses an optional array_specifier returning `Ok(None)` if there is no
/// LeftBracket
fn parse_array_specifier(&mut self) -> Result<Option<ArraySize>> {
// TODO: expressions
if let Some(&TokenValue::LeftBracket) = self.lexer.peek().map(|t| &t.value) {
self.bump()?;
self.expect(TokenValue::RightBracket)?;
Ok(Some(ArraySize::Dynamic))
} else {
Ok(None)
}
}
fn parse_type(&mut self) -> Result<(Option<Handle<Type>>, SourceMetadata)> {
let token = self.bump()?;
let ty = match token.value {
TokenValue::Void => None,
TokenValue::TypeName(ty) => Some(ty),
TokenValue::Struct => todo!(),
_ => return Err(ErrorKind::InvalidToken(token)),
};
let handle = ty.map(|t| self.program.module.types.fetch_or_append(t));
let size = self.parse_array_specifier()?;
Ok((handle.map(|ty| self.maybe_array(ty, size)), token.meta))
}
fn parse_type_non_void(&mut self) -> Result<(Handle<Type>, SourceMetadata)> {
let (maybe_ty, meta) = self.parse_type()?;
let ty =
maybe_ty.ok_or_else(|| ErrorKind::SemanticError(meta, "Type can't be void".into()))?;
Ok((ty, meta))
}
fn maybe_array(&mut self, base: Handle<Type>, size: Option<ArraySize>) -> Handle<Type> {
size.map(|size| {
self.program.module.types.fetch_or_append(Type {
name: None,
inner: TypeInner::Array {
base,
size,
stride: self.program.module.types[base]
.inner
.span(&self.program.module.constants),
},
})
})
.unwrap_or(base)
}
fn peek_type_qualifier(&mut self) -> bool {
self.lexer.peek().map_or(false, |t| match t.value {
TokenValue::Interpolation(_)
| TokenValue::Sampling(_)
| TokenValue::Const
| TokenValue::In
| TokenValue::Out
| TokenValue::Uniform
| TokenValue::Layout => true,
_ => false,
})
}
fn parse_type_qualifiers(&mut self) -> Result<Vec<TypeQualifier>> {
let mut qualifiers = Vec::new();
while self.peek_type_qualifier() {
let token = self.bump()?;
// Handle layout qualifiers outside the match since this can push multiple values
if token.value == TokenValue::Layout {
self.parse_layout_qualifier_id_list(&mut qualifiers)?;
continue;
}
qualifiers.push(match token.value {
TokenValue::Interpolation(i) => TypeQualifier::Interpolation(i),
TokenValue::Const => TypeQualifier::StorageQualifier(StorageQualifier::Const),
TokenValue::In => TypeQualifier::StorageQualifier(StorageQualifier::Input),
TokenValue::Out => TypeQualifier::StorageQualifier(StorageQualifier::Output),
TokenValue::Uniform => TypeQualifier::StorageQualifier(
StorageQualifier::StorageClass(StorageClass::Uniform),
),
TokenValue::Sampling(s) => TypeQualifier::Sampling(s),
_ => unreachable!(),
})
}
Ok(qualifiers)
}
fn parse_layout_qualifier_id_list(
&mut self,
qualifiers: &mut Vec<TypeQualifier>,
) -> Result<()> {
// We need both of these to produce a ResourceBinding
let mut group = None;
let mut binding = None;
self.expect(TokenValue::LeftParen)?;
loop {
self.parse_layout_qualifier_id(qualifiers, &mut group, &mut binding)?;
if self.bump_if(TokenValue::Comma).is_some() {
continue;
}
break;
}
self.expect(TokenValue::RightParen)?;
match (group, binding) {
(Some((group, _)), Some((binding, _))) => {
qualifiers.push(TypeQualifier::ResourceBinding(ResourceBinding {
group,
binding,
}))
}
// Produce an error if we have one of group or binding but not the other
(Some((_, meta)), None) => {
return Err(ErrorKind::SemanticError(
meta,
"set specified with no binding".into(),
))
}
(None, Some((_, meta))) => {
return Err(ErrorKind::SemanticError(
meta,
"binding specified with no set".into(),
))
}
(None, None) => (),
}
Ok(())
}
fn parse_uint_constant(&mut self) -> Result<(u32, SourceMetadata)> {
let (value, meta) = self.parse_constant_expression()?;
let int = match self.program.module.constants[value].inner {
ConstantInner::Scalar {
value: ScalarValue::Uint(int),
..
} => u32::try_from(int)
.map_err(|_| ErrorKind::SemanticError(meta, "int constant overflows".into())),
ConstantInner::Scalar {
value: ScalarValue::Sint(int),
..
} => u32::try_from(int)
.map_err(|_| ErrorKind::SemanticError(meta, "int constant overflows".into())),
_ => Err(ErrorKind::SemanticError(
meta,
"Expected a uint constant".into(),
)),
}?;
Ok((int, meta))
}
fn parse_layout_qualifier_id(
&mut self,
qualifiers: &mut Vec<TypeQualifier>,
group: &mut Option<(u32, SourceMetadata)>,
binding: &mut Option<(u32, SourceMetadata)>,
) -> Result<()> {
// layout_qualifier_id:
// IDENTIFIER
// IDENTIFIER EQUAL constant_expression
// SHARED
let mut token = self.bump()?;
match token.value {
TokenValue::Identifier(name) => {
if self.bump_if(TokenValue::Assign).is_some() {
let (value, end_meta) = self.parse_uint_constant()?;
token.meta = token.meta.union(&end_meta);
match name.as_str() {
"location" => qualifiers.push(TypeQualifier::Location(value)),
"set" => *group = Some((value, end_meta)),
"binding" => *binding = Some((value, end_meta)),
_ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)),
}
} else {
match name.as_str() {
"push_constant" => {
qualifiers.push(TypeQualifier::StorageQualifier(
StorageQualifier::StorageClass(StorageClass::PushConstant),
));
qualifiers.push(TypeQualifier::Layout(StructLayout::Std430));
}
"std140" => qualifiers.push(TypeQualifier::Layout(StructLayout::Std140)),
"std430" => qualifiers.push(TypeQualifier::Layout(StructLayout::Std430)),
"early_fragment_tests" => {
qualifiers.push(TypeQualifier::EarlyFragmentTests)
}
_ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)),
}
};
Ok(())
}
// TODO: handle Shared?
_ => Err(ErrorKind::InvalidToken(token)),
}
}
fn parse_constant_expression(&mut self) -> Result<(Handle<Constant>, SourceMetadata)> {
let mut expressions = Arena::new();
let mut locals = Arena::new();
let mut arguments = Vec::new();
let mut block = Block::new();
let mut ctx = Context::new(
self.program,
&mut block,
&mut expressions,
&mut locals,
&mut arguments,
);
let expr = self.parse_conditional(&mut ctx, &mut block, None)?;
let (root, meta) = ctx.lower_expect(self.program, expr, false, &mut block)?;
Ok((self.program.solve_constant(&ctx, root, meta)?, meta))
}
fn parse_external_declaration(&mut self) -> Result<()> {
// TODO: Create body and expressions arena to be used in all entry
// points to handle this case
// ```glsl
// // This is valid and the body of main will contain the assignment
// float b;
// float a = b = 1;
//
// void main() {}
// ```
let (mut e, mut l, mut a) = (Arena::new(), Arena::new(), Vec::new());
let mut body = Block::new();
let mut ctx = Context::new(self.program, &mut body, &mut e, &mut l, &mut a);
if !self.parse_declaration(&mut ctx, &mut body, true)? {
let token = self.bump()?;
match token.value {
TokenValue::Semicolon if self.program.version == 460 => Ok(()),
_ => Err(ErrorKind::InvalidToken(token)),
}
} else {
Ok(())
}
}
fn peek_type_name(&mut self) -> bool {
self.lexer.peek().map_or(false, |t| match t.value {
TokenValue::TypeName(_) | TokenValue::Void => true,
_ => false,
})
}
fn peek_parameter_qualifier(&mut self) -> bool {
self.lexer.peek().map_or(false, |t| match t.value {
TokenValue::In | TokenValue::Out | TokenValue::InOut | TokenValue::Const => true,
_ => false,
})
}
/// Returns the parsed `ParameterQualifier` or `ParameterQualifier::In`
fn parse_parameter_qualifier(&mut self) -> ParameterQualifier {
if self.peek_parameter_qualifier() {
match self.bump().unwrap().value {
TokenValue::In => ParameterQualifier::In,
TokenValue::Out => ParameterQualifier::Out,
TokenValue::InOut => ParameterQualifier::InOut,
TokenValue::Const => ParameterQualifier::Const,
_ => unreachable!(),
}
} else {
ParameterQualifier::In
}
}
fn parse_initializer(
&mut self,
ty: Handle<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(TokenValue::LeftBrace) {
// initializer_list
let mut components = Vec::new();
loop {
// TODO: Change type
components.push(self.parse_initializer(ty, ctx, body)?.0);
let token = self.bump()?;
match token.value {
TokenValue::Comma => {
if let Some(Token { meta: end_meta, .. }) =
self.bump_if(TokenValue::RightBrace)
{
meta = meta.union(&end_meta);
break;
}
}
TokenValue::RightBrace => {
meta = meta.union(&token.meta);
break;
}
_ => return Err(ErrorKind::InvalidToken(token)),
}
}
Ok((
ctx.add_expression(Expression::Compose { ty, components }, body),
meta,
))
} else {
let expr = self.parse_assignment(ctx, body)?;
Ok(ctx.lower_expect(self.program, expr, false, body)?)
}
}
// Note: caller preparsed the type and qualifiers
// Note: caller skips this if the fallthrough token is not expected to be consumed here so this
// produced Error::InvalidToken if it isn't consumed
fn parse_init_declarator_list(
&mut self,
ty: Handle<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.lexer.peek())
.filter(|t| t.value == TokenValue::Comma)
.is_some()
{
fallthrough.take().or_else(|| self.lexer.next());
}
loop {
let token = fallthrough
.take()
.ok_or(ErrorKind::EndOfFile)
.or_else(|_| self.bump())?;
let name = match token.value {
TokenValue::Semicolon => break,
TokenValue::Identifier(name) => name,
_ => return Err(ErrorKind::InvalidToken(token)),
};
let mut meta = token.meta;
// array_specifier
// array_specifier EQUAL initializer
// EQUAL initializer
// parse an array specifier if it exists
// NOTE: unlike other parse methods this one doesn't expect an array specifier and
// returns Ok(None) rather than an error if there is not one
let array_specifier = self.parse_array_specifier()?;
let ty = self.maybe_array(ty, array_specifier);
let init = self
.bump_if(TokenValue::Assign)
.map::<Result<_>, _>(|_| {
let (expr, init_meta) = self.parse_initializer(ty, ctx.ctx, ctx.body)?;
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
.clone()
.and_then(|(root, meta)| self.program.solve_constant(ctx.ctx, root, meta).ok());
let pointer = ctx.add_var(self.program, ty, name, maybe_constant, meta)?;
if let Some((value, _)) = init {
ctx.flush_expressions();
ctx.body.push(Statement::Store { pointer, value });
}
let token = self.bump()?;
match token.value {
TokenValue::Semicolon => break,
TokenValue::Comma => {}
_ => return Err(ErrorKind::InvalidToken(token)),
}
}
Ok(())
}
/// `external` whether or not we are in a global or local context
fn parse_declaration(
&mut self,
ctx: &mut Context,
body: &mut Block,
external: bool,
) -> Result<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() || self.peek_type_name() {
let qualifiers = self.parse_type_qualifiers()?;
if self.peek_type_name() {
// This branch handles variables and function prototypes and if
// external is true also function definitions
let (ty, mut meta) = self.parse_type()?;
let token = self.bump()?;
let token_fallthrough = match token.value {
TokenValue::Identifier(name) => match self.expect_peek()?.value {
TokenValue::LeftParen => {
// This branch handles function definition and prototypes
self.bump()?;
let result = ty.map(|ty| FunctionResult { ty, binding: None });
let mut expressions = Arena::new();
let mut local_variables = Arena::new();
let mut arguments = Vec::new();
let mut parameters = Vec::new();
let mut body = Block::new();
let mut sig = FunctionSignature {
name: name.clone(),
parameters: Vec::new(),
};
let mut context = Context::new(
self.program,
&mut body,
&mut expressions,
&mut local_variables,
&mut arguments,
);
self.parse_function_args(
&mut context,
&mut body,
&mut parameters,
&mut sig,
)?;
let end_meta = self.expect(TokenValue::RightParen)?.meta;
meta = meta.union(&end_meta);
let token = self.bump()?;
return match token.value {
TokenValue::Semicolon => {
// This branch handles function prototypes
self.program.add_prototype(
Function {
name: Some(name),
result,
arguments,
..Default::default()
},
sig,
parameters,
meta,
)?;
Ok(true)
}
TokenValue::LeftBrace if external => {
// This branch handles function definitions
// as you can see by the guard this branch
// only happens if external is also true
// parse the body
self.parse_compound_statement(&mut context, &mut body)?;
let Context { arg_use, .. } = context;
let handle = self.program.add_function(
Function {
name: Some(name),
result,
expressions,
local_variables,
arguments,
body,
},
sig,
parameters,
meta,
)?;
self.program.function_arg_use[handle.index()] = arg_use;
Ok(true)
}
_ => Err(ErrorKind::InvalidToken(token)),
};
}
// Pass the token to the init_declator_list parser
_ => Token {
value: TokenValue::Identifier(name),
meta: token.meta,
},
},
// Pass the token to the init_declator_list parser
_ => token,
};
// If program execution has reached here then this will be a
// init_declarator_list
// token_falltrough will have a token that was already bumped
if let Some(ty) = ty {
let mut ctx = DeclarationContext {
qualifiers,
external,
ctx,
body,
};
self.parse_init_declarator_list(ty, Some(token_fallthrough), &mut ctx)?;
} else {
return Err(ErrorKind::SemanticError(
meta,
"Declaration cannot have void type".into(),
));
}
Ok(true)
} else {
// This branch handles struct definitions and modifiers like
// ```glsl
// layout(early_fragment_tests);
// ```
let token = self.bump()?;
match token.value {
TokenValue::Identifier(ty_name) => {
if self.bump_if(TokenValue::LeftBrace).is_some() {
self.parse_block_declaration(&qualifiers, ty_name, token.meta)
} else {
//TODO: declaration
// type_qualifier IDENTIFIER SEMICOLON
// type_qualifier IDENTIFIER identifier_list SEMICOLON
todo!()
}
}
TokenValue::Semicolon => Ok(true),
_ => Err(ErrorKind::InvalidToken(token)),
}
}
} else {
// TODO: Handle precision qualifiers
Ok(false)
}
}
fn parse_block_declaration(
&mut self,
qualifiers: &[TypeQualifier],
ty_name: String,
meta: SourceMetadata,
) -> Result<bool> {
let mut class = StorageClass::Private;
let mut binding = None;
let mut layout = None;
for qualifier in qualifiers {
match *qualifier {
TypeQualifier::StorageQualifier(StorageQualifier::StorageClass(c)) => {
if StorageClass::PushConstant == class && c == StorageClass::Uniform {
// Ignore the Uniform qualifier if the class was already set to PushConstant
continue;
} else if StorageClass::Private != class {
return Err(ErrorKind::SemanticError(
meta,
"Cannot use more than one storage qualifier per declaration".into(),
));
}
class = c;
}
TypeQualifier::ResourceBinding(ref r) => {
if binding.is_some() {
return Err(ErrorKind::SemanticError(
meta,
"Cannot use more than one storage qualifier per declaration".into(),
));
}
binding = Some(r.clone());
}
TypeQualifier::Layout(ref l) => {
if layout.is_some() {
return Err(ErrorKind::SemanticError(
meta,
"Cannot use more than one storage qualifier per declaration".into(),
));
}
layout = Some(l);
}
_ => {
return Err(ErrorKind::SemanticError(
meta,
"Qualifier not supported in block declarations".into(),
));
}
}
}
let mut members = Vec::new();
let span = self.parse_struct_declaration_list(&mut members)?;
self.expect(TokenValue::RightBrace)?;
let mut ty = self.program.module.types.append(Type {
name: Some(ty_name),
inner: TypeInner::Struct {
top_level: true,
members: members.clone(),
span,
},
});
let token = self.bump()?;
let name = match token.value {
TokenValue::Semicolon => None,
TokenValue::Identifier(name) => {
if let Some(size) = self.parse_array_specifier()? {
ty = self.program.module.types.append(Type {
name: None,
inner: TypeInner::Array {
base: ty,
size,
stride: self.program.module.types[ty]
.inner
.span(&self.program.module.constants),
},
});
}
self.expect(TokenValue::Semicolon)?;
Some(name)
}
_ => return Err(ErrorKind::InvalidToken(token)),
};
let handle = self.program.module.global_variables.append(GlobalVariable {
name: name.clone(),
class,
binding,
ty,
init: None,
storage_access: StorageAccess::empty(),
});
if let Some(k) = name {
self.program.global_variables.push((
k,
GlobalLookup {
kind: GlobalLookupKind::Variable(handle),
entry_arg: None,
},
));
}
for (i, k) in members
.into_iter()
.enumerate()
.filter_map(|(i, m)| m.name.map(|s| (i as u32, s)))
{
self.program.global_variables.push((
k,
GlobalLookup {
kind: GlobalLookupKind::BlockSelect(handle, i),
entry_arg: None,
},
));
}
Ok(true)
}
// TODO: Accept layout arguments
fn parse_struct_declaration_list(&mut self, members: &mut Vec<StructMember>) -> Result<u32> {
let mut span = 0;
loop {
// TODO: type_qualifier
if !self.peek_type_name() {
break;
}
let ty = self.parse_type_non_void()?.0;
let name = self.expect_ident()?.0;
self.expect(TokenValue::Semicolon)?;
members.push(StructMember {
name: Some(name),
ty,
binding: None,
offset: span,
});
span = self.program.module.types[ty]
.inner
.span(&self.program.module.constants);
}
Ok(span)
}
fn parse_primary(&mut self, ctx: &mut Context, body: &mut Block) -> Result<Handle<HirExpr>> {
let mut token = self.bump()?;
let (width, value) = match token.value {
TokenValue::IntConstant(int) => (
(int.width / 8) as u8,
if int.signed {
ScalarValue::Sint(int.value as i64)
} else {
ScalarValue::Uint(int.value)
},
),
TokenValue::FloatConstant(float) => (
(float.width / 8) as u8,
ScalarValue::Float(float.value as f64),
),
TokenValue::BoolConstant(value) => (1, ScalarValue::Bool(value)),
TokenValue::LeftParen => {
let expr = self.parse_expression(ctx, body)?;
let meta = self.expect(TokenValue::RightParen)?.meta;
token.meta = token.meta.union(&meta);
return Ok(expr);
}
_ => return Err(ErrorKind::InvalidToken(token)),
};
let handle = self.program.module.constants.append(Constant {
name: None,
specialization: None,
inner: ConstantInner::Scalar { width, value },
});
Ok(ctx.hir_exprs.append(HirExpr {
kind: HirExprKind::Constant(handle),
meta: token.meta,
}))
}
fn parse_function_call_args(
&mut self,
ctx: &mut Context,
body: &mut Block,
meta: &mut SourceMetadata,
) -> Result<Vec<Handle<HirExpr>>> {
let mut args = Vec::new();
if let Some(token) = self.bump_if(TokenValue::RightParen) {
*meta = meta.union(&token.meta);
} else {
loop {
args.push(self.parse_assignment(ctx, body)?);
let token = self.bump()?;
match token.value {
TokenValue::Comma => {}
TokenValue::RightParen => {
*meta = meta.union(&token.meta);
break;
}
_ => return Err(ErrorKind::InvalidToken(token)),
}
}
}
Ok(args)
}
fn parse_postfix(&mut self, ctx: &mut Context, body: &mut Block) -> Result<Handle<HirExpr>> {
let mut base = match self.expect_peek()?.value {
TokenValue::Identifier(_) => {
let (name, mut meta) = self.expect_ident()?;
let expr = if self.bump_if(TokenValue::LeftParen).is_some() {
let args = self.parse_function_call_args(ctx, body, &mut meta)?;
HirExpr {
kind: HirExprKind::Call(FunctionCall {
kind: FunctionCallKind::Function(name),
args,
}),
meta,
}
} else {
let var = match self.program.lookup_variable(ctx, body, &name)? {
Some(var) => var,
None => return Err(ErrorKind::UnknownVariable(meta, name)),
};
HirExpr {
kind: HirExprKind::Variable(var),
meta,
}
};
ctx.hir_exprs.append(expr)
}
TokenValue::TypeName(_) => {
let Token { value, mut meta } = self.bump()?;
let handle = if let TokenValue::TypeName(ty) = value {
self.program.module.types.fetch_or_append(ty)
} else {
unreachable!()
};
self.expect(TokenValue::LeftParen)?;
let args = self.parse_function_call_args(ctx, body, &mut meta)?;
ctx.hir_exprs.append(HirExpr {
kind: HirExprKind::Call(FunctionCall {
kind: FunctionCallKind::TypeConstructor(handle),
args,
}),
meta,
})
}
_ => self.parse_primary(ctx, body)?,
};
// TODO: postfix inc/dec
while let TokenValue::LeftBracket | TokenValue::Dot = self.expect_peek()?.value {
let Token { value, meta } = self.bump()?;
match value {
TokenValue::LeftBracket => {
let index = self.parse_expression(ctx, body)?;
let end_meta = self.expect(TokenValue::RightBracket)?.meta;
base = ctx.hir_exprs.append(HirExpr {
kind: HirExprKind::Access { base, index },
meta: meta.union(&end_meta),
})
}
TokenValue::Dot => {
let (field, end_meta) = self.expect_ident()?;
base = ctx.hir_exprs.append(HirExpr {
kind: HirExprKind::Select { base, field },
meta: meta.union(&end_meta),
})
}
_ => unreachable!(),
}
}
Ok(base)
}
fn parse_unary(&mut self, ctx: &mut Context, body: &mut Block) -> Result<Handle<HirExpr>> {
// TODO: prefix inc/dec
Ok(match self.expect_peek()?.value {
TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => {
let Token { value, meta } = self.bump()?;
let expr = self.parse_unary(ctx, body)?;
let end_meta = ctx.hir_exprs[expr].meta;
let kind = match value {
TokenValue::Dash => HirExprKind::Unary {
op: UnaryOperator::Negate,
expr,
},
TokenValue::Bang | TokenValue::Tilde => HirExprKind::Unary {
op: UnaryOperator::Not,
expr,
},
_ => return Ok(expr),
};
ctx.hir_exprs.append(HirExpr {
kind,
meta: meta.union(&end_meta),
})
}
_ => self.parse_postfix(ctx, body)?,
})
}
fn parse_binary(
&mut self,
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(ctx, body))?;
let start_meta = ctx.hir_exprs[left].meta;
while let Some((l_bp, r_bp)) = binding_power(&self.expect_peek()?.value) {
if l_bp < min_bp {
break;
}
let Token { value, .. } = self.bump()?;
let right = self.parse_binary(ctx, body, None, r_bp)?;
let end_meta = ctx.hir_exprs[right].meta;
left = ctx.hir_exprs.append(HirExpr {
kind: HirExprKind::Binary {
left,
op: match value {
TokenValue::LogicalOr => BinaryOperator::LogicalOr,
TokenValue::LogicalXor => BinaryOperator::NotEqual,
TokenValue::LogicalAnd => BinaryOperator::LogicalAnd,
TokenValue::VerticalBar => BinaryOperator::InclusiveOr,
TokenValue::Caret => BinaryOperator::ExclusiveOr,
TokenValue::Ampersand => BinaryOperator::And,
TokenValue::Equal => BinaryOperator::Equal,
TokenValue::NotEqual => BinaryOperator::NotEqual,
TokenValue::GreaterEqual => BinaryOperator::GreaterEqual,
TokenValue::LessEqual => BinaryOperator::LessEqual,
TokenValue::LeftAngle => BinaryOperator::Greater,
TokenValue::RightAngle => BinaryOperator::Less,
TokenValue::LeftShift => BinaryOperator::ShiftLeft,
TokenValue::RightShift => BinaryOperator::ShiftRight,
TokenValue::Plus => BinaryOperator::Add,
TokenValue::Dash => BinaryOperator::Subtract,
TokenValue::Star => BinaryOperator::Multiply,
TokenValue::Slash => BinaryOperator::Divide,
TokenValue::Percent => BinaryOperator::Modulo,
_ => unreachable!(),
},
right,
},
meta: start_meta.union(&end_meta),
})
}
Ok(left)
}
fn parse_conditional(
&mut self,
ctx: &mut Context,
body: &mut Block,
passtrough: Option<Handle<HirExpr>>,
) -> Result<Handle<HirExpr>> {
let mut condition = self.parse_binary(ctx, body, passtrough, 0)?;
let start_meta = ctx.hir_exprs[condition].meta;
if self.bump_if(TokenValue::Question).is_some() {
let accept = self.parse_expression(ctx, body)?;
self.expect(TokenValue::Colon)?;
let reject = self.parse_assignment(ctx, body)?;
let end_meta = ctx.hir_exprs[reject].meta;
condition = ctx.hir_exprs.append(HirExpr {
kind: HirExprKind::Conditional {
condition,
accept,
reject,
},
meta: start_meta.union(&end_meta),
})
}
Ok(condition)
}
fn parse_assignment(&mut self, ctx: &mut Context, body: &mut Block) -> Result<Handle<HirExpr>> {
let tgt = self.parse_unary(ctx, body)?;
let start_meta = ctx.hir_exprs[tgt].meta;
Ok(match self.expect_peek()?.value {
TokenValue::Assign => {
self.bump()?;
let value = self.parse_assignment(ctx, body)?;
let end_meta = ctx.hir_exprs[value].meta;
ctx.hir_exprs.append(HirExpr {
kind: HirExprKind::Assign { tgt, value },
meta: start_meta.union(&end_meta),
})
}
TokenValue::OrAssign
| TokenValue::AndAssign
| TokenValue::AddAssign
| TokenValue::DivAssign
| TokenValue::ModAssign
| TokenValue::SubAssign
| TokenValue::MulAssign
| TokenValue::LeftShiftAssign
| TokenValue::RightShiftAssign
| TokenValue::XorAssign => {
let token = self.bump()?;
let right = self.parse_assignment(ctx, body)?;
let end_meta = ctx.hir_exprs[right].meta;
let value = ctx.hir_exprs.append(HirExpr {
meta: start_meta.union(&end_meta),
kind: HirExprKind::Binary {
left: tgt,
op: match token.value {
TokenValue::OrAssign => BinaryOperator::InclusiveOr,
TokenValue::AndAssign => BinaryOperator::And,
TokenValue::AddAssign => BinaryOperator::Add,
TokenValue::DivAssign => BinaryOperator::Divide,
TokenValue::ModAssign => BinaryOperator::Modulo,
TokenValue::SubAssign => BinaryOperator::Subtract,
TokenValue::MulAssign => BinaryOperator::Multiply,
TokenValue::LeftShiftAssign => BinaryOperator::ShiftLeft,
TokenValue::RightShiftAssign => BinaryOperator::ShiftRight,
TokenValue::XorAssign => BinaryOperator::ExclusiveOr,
_ => unreachable!(),
},
right,
},
});
ctx.hir_exprs.append(HirExpr {
kind: HirExprKind::Assign { tgt, value },
meta: start_meta.union(&end_meta),
})
}
_ => self.parse_conditional(ctx, body, Some(tgt))?,
})
}
fn parse_expression(&mut self, ctx: &mut Context, body: &mut Block) -> Result<Handle<HirExpr>> {
let mut expr = self.parse_assignment(ctx, body)?;
while let TokenValue::Comma = self.expect_peek()?.value {
self.bump()?;
expr = self.parse_assignment(ctx, body)?;
}
Ok(expr)
}
fn parse_statement(&mut self, ctx: &mut Context, body: &mut Block) -> Result<()> {
match self.expect_peek()?.value {
TokenValue::Continue => {
self.bump()?;
body.push(Statement::Continue);
self.expect(TokenValue::Semicolon)?;
}
TokenValue::Break => {
self.bump()?;
body.push(Statement::Break);
self.expect(TokenValue::Semicolon)?;
}
TokenValue::Return => {
self.bump()?;
let value = match self.expect_peek()?.value {
TokenValue::Semicolon => {
self.bump()?;
None
}
_ => {
// TODO: Implicit conversions
let expr = self.parse_expression(ctx, body)?;
self.expect(TokenValue::Semicolon)?;
Some(ctx.lower_expect(self.program, expr, false, body)?.0)
}
};
ctx.emit_flush(body);
ctx.emit_start();
body.push(Statement::Return { value })
}
TokenValue::Discard => {
self.bump()?;
body.push(Statement::Kill);
self.expect(TokenValue::Semicolon)?;
}
TokenValue::If => {
self.bump()?;
self.expect(TokenValue::LeftParen)?;
let condition = {
let expr = self.parse_expression(ctx, body)?;
ctx.lower_expect(self.program, expr, false, body)?.0
};
self.expect(TokenValue::RightParen)?;
ctx.emit_flush(body);
ctx.emit_start();
let mut accept = Block::new();
self.parse_statement(ctx, &mut accept)?;
let mut reject = Block::new();
if self.bump_if(TokenValue::Else).is_some() {
self.parse_statement(ctx, &mut reject)?;
}
body.push(Statement::If {
condition,
accept,
reject,
});
}
TokenValue::Switch => {
self.bump()?;
self.expect(TokenValue::LeftParen)?;
// TODO: Implicit conversions
let selector = {
let expr = self.parse_expression(ctx, body)?;
ctx.lower_expect(self.program, expr, false, body)?.0
};
self.expect(TokenValue::RightParen)?;
ctx.emit_flush(body);
ctx.emit_start();
let mut cases = Vec::new();
let mut default = Block::new();
self.expect(TokenValue::LeftBrace)?;
loop {
match self.expect_peek()?.value {
TokenValue::Case => {
self.bump()?;
let value = {
let expr = self.parse_expression(ctx, body)?;
let (root, meta) =
ctx.lower_expect(self.program, expr, false, body)?;
let constant = self.program.solve_constant(&ctx, root, meta)?;
match self.program.module.constants[constant].inner {
ConstantInner::Scalar {
value: ScalarValue::Sint(int),
..
} => int as i32,
ConstantInner::Scalar {
value: ScalarValue::Uint(int),
..
} => int as i32,
_ => {
return Err(ErrorKind::SemanticError(
meta,
"Case values can only be integers".into(),
))
}
}
};
self.expect(TokenValue::Colon)?;
let mut body = Block::new();
loop {
match self.expect_peek()?.value {
TokenValue::Case
| TokenValue::Default
| TokenValue::RightBrace => break,
_ => self.parse_statement(ctx, &mut body)?,
}
}
let fall_through = body.iter().any(|s| {
mem::discriminant(s) == mem::discriminant(&Statement::Break)
});
cases.push(SwitchCase {
value,
body,
fall_through,
})
}
TokenValue::Default => {
let Token { meta, .. } = self.bump()?;
self.expect(TokenValue::Colon)?;
if !default.is_empty() {
return Err(ErrorKind::SemanticError(
meta,
"Can only have one default case per switch statement".into(),
));
}
loop {
match self.expect_peek()?.value {
TokenValue::Case | TokenValue::RightBrace => break,
_ => self.parse_statement(ctx, &mut &mut default)?,
}
}
}
TokenValue::RightBrace => {
self.bump()?;
break;
}
_ => return Err(ErrorKind::InvalidToken(self.bump()?)),
}
}
body.push(Statement::Switch {
selector,
cases,
default,
});
}
TokenValue::While => {
self.bump()?;
let mut loop_body = Block::new();
self.expect(TokenValue::LeftParen)?;
let root = self.parse_expression(ctx, &mut loop_body)?;
self.expect(TokenValue::RightParen)?;
let expr = ctx
.lower_expect(self.program, root, false, &mut loop_body)?
.0;
let condition = ctx.add_expression(
Expression::Unary {
op: UnaryOperator::Not,
expr,
},
&mut loop_body,
);
ctx.emit_flush(&mut loop_body);
ctx.emit_start();
loop_body.push(Statement::If {
condition,
accept: vec![Statement::Break],
reject: Block::new(),
});
self.parse_statement(ctx, &mut loop_body)?;
body.push(Statement::Loop {
body: loop_body,
continuing: Block::new(),
})
}
TokenValue::Do => {
self.bump()?;
let mut loop_body = Block::new();
self.parse_statement(ctx, &mut loop_body)?;
self.expect(TokenValue::While)?;
self.expect(TokenValue::LeftParen)?;
let root = self.parse_expression(ctx, &mut loop_body)?;
self.expect(TokenValue::RightParen)?;
let expr = ctx
.lower_expect(self.program, root, false, &mut loop_body)?
.0;
let condition = ctx.add_expression(
Expression::Unary {
op: UnaryOperator::Not,
expr,
},
&mut loop_body,
);
ctx.emit_flush(&mut loop_body);
ctx.emit_start();
loop_body.push(Statement::If {
condition,
accept: vec![Statement::Break],
reject: Block::new(),
});
body.push(Statement::Loop {
body: loop_body,
continuing: Block::new(),
})
}
TokenValue::For => {
self.bump()?;
ctx.push_scope();
self.expect(TokenValue::LeftParen)?;
if self.bump_if(TokenValue::Semicolon).is_none() {
if self.peek_type_name() || self.peek_type_qualifier() {
self.parse_declaration(ctx, body, false)?;
} else {
self.parse_expression(ctx, body)?;
self.expect(TokenValue::Semicolon)?;
}
}
ctx.emit_flush(body);
ctx.emit_start();
let (mut block, mut continuing) = (Block::new(), Block::new());
if self.bump_if(TokenValue::Semicolon).is_none() {
let expr = if self.peek_type_name() || self.peek_type_qualifier() {
let qualifiers = self.parse_type_qualifiers()?;
let (ty, meta) = self.parse_type_non_void()?;
let name = self.expect_ident()?.0;
self.expect(TokenValue::Assign)?;
let (value, end_meta) = self.parse_initializer(ty, ctx, &mut block)?;
let decl = VarDeclaration {
qualifiers: &qualifiers,
ty,
name,
init: None,
meta: meta.union(&end_meta),
};
let pointer = self.program.add_local_var(ctx, &mut block, decl)?;
ctx.emit_flush(&mut block);
ctx.emit_start();
block.push(Statement::Store { pointer, value });
value
} else {
let root = self.parse_expression(ctx, &mut block)?;
ctx.lower_expect(self.program, root, false, &mut block)?.0
};
let condition = ctx.add_expression(
Expression::Unary {
op: UnaryOperator::Not,
expr,
},
&mut block,
);
ctx.emit_flush(&mut block);
ctx.emit_start();
block.push(Statement::If {
condition,
accept: vec![Statement::Break],
reject: Block::new(),
});
self.expect(TokenValue::Semicolon)?;
}
match self.expect_peek()?.value {
TokenValue::RightParen => {}
_ => {
let rest = self.parse_expression(ctx, &mut continuing)?;
ctx.lower(self.program, rest, false, &mut continuing)?;
}
}
self.expect(TokenValue::RightParen)?;
self.parse_statement(ctx, &mut block)?;
body.push(Statement::Loop {
body: block,
continuing,
});
ctx.remove_current_scope();
}
TokenValue::LeftBrace => {
self.bump()?;
let mut block = Block::new();
ctx.push_scope();
self.parse_compound_statement(ctx, &mut block)?;
ctx.remove_current_scope();
body.push(Statement::Block(block));
}
// TODO: We should also add TypeName here because this is valid
// ```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
TokenValue::Plus
| TokenValue::Dash
| TokenValue::Bang
| TokenValue::Tilde
| TokenValue::LeftParen
| TokenValue::Identifier(_)
| TokenValue::IntConstant(_)
| TokenValue::BoolConstant(_)
| TokenValue::FloatConstant(_) => {
let expr = self.parse_expression(ctx, body)?;
ctx.lower(self.program, expr, false, body)?;
self.expect(TokenValue::Semicolon)?;
}
TokenValue::Semicolon => {
self.bump()?;
}
_ => {
if self.peek_type_name() || self.peek_type_qualifier() {
self.parse_declaration(ctx, body, false)?;
}
}
}
Ok(())
}
fn parse_compound_statement(&mut self, ctx: &mut Context, body: &mut Block) -> Result<()> {
loop {
if self.bump_if(TokenValue::RightBrace).is_some() {
break;
}
self.parse_statement(ctx, body)?;
}
Ok(())
}
fn parse_function_args(
&mut self,
context: &mut Context,
body: &mut Block,
parameters: &mut Vec<ParameterQualifier>,
sig: &mut FunctionSignature,
) -> Result<()> {
loop {
if self.peek_type_name() || self.peek_parameter_qualifier() {
let qualifier = self.parse_parameter_qualifier();
parameters.push(qualifier);
let ty = self.parse_type_non_void()?.0;
match self.expect_peek()?.value {
TokenValue::Comma => {
self.bump()?;
context.add_function_arg(&mut self.program, sig, body, None, ty, qualifier);
continue;
}
TokenValue::Identifier(_) => {
let name = self.expect_ident()?.0;
let size = self.parse_array_specifier()?;
let ty = self.maybe_array(ty, size);
context.add_function_arg(
&mut self.program,
sig,
body,
Some(name),
ty,
qualifier,
);
if self.bump_if(TokenValue::Comma).is_some() {
continue;
}
break;
}
_ => break,
}
}
break;
}
Ok(())
}
}
struct DeclarationContext<'ctx, 'fun> {
qualifiers: Vec<TypeQualifier>,
external: bool,
ctx: &'ctx mut Context<'fun>,
body: &'ctx mut Block,
}
impl<'ctx, 'fun> DeclarationContext<'ctx, 'fun> {
fn add_var(
&mut self,
program: &mut Program,
ty: Handle<Type>,
name: String,
init: Option<Handle<Constant>>,
meta: SourceMetadata,
) -> Result<Handle<Expression>> {
let decl = VarDeclaration {
qualifiers: &self.qualifiers,
ty,
name,
init,
meta,
};
match self.external {
true => program.add_global_var(self.ctx, self.body, decl),
false => program.add_local_var(self.ctx, self.body, decl),
}
}
fn flush_expressions(&mut self) {
self.ctx.emit_flush(self.body);
self.ctx.emit_start()
}
}
fn binding_power(value: &TokenValue) -> Option<(u8, u8)> {
Some(match *value {
TokenValue::LogicalOr => (1, 2),
TokenValue::LogicalXor => (3, 4),
TokenValue::LogicalAnd => (5, 6),
TokenValue::VerticalBar => (7, 8),
TokenValue::Caret => (9, 10),
TokenValue::Ampersand => (11, 12),
TokenValue::Equal | TokenValue::NotEqual => (13, 14),
TokenValue::GreaterEqual
| TokenValue::LessEqual
| TokenValue::LeftAngle
| TokenValue::RightAngle => (15, 16),
TokenValue::LeftShift | TokenValue::RightShift => (17, 18),
TokenValue::Plus | TokenValue::Dash => (19, 20),
TokenValue::Star | TokenValue::Slash | TokenValue::Percent => (21, 22),
_ => return None,
})
}