/// State for constructing an AST expression. /// /// Not to be confused with [`lower::ExpressionContext`], which is for producing /// Naga IR from the AST we produce here. /// /// [`lower::ExpressionContext`]: super::lower::ExpressionContext struct ExpressionContext<'input, 'temp, 'out> { /// The [`TranslationUnit::expressions`] arena to which we should contribute /// expressions. /// /// [`TranslationUnit::expressions`]: ast::TranslationUnit::expressions
expressions: &'out mut Arena<ast::Expression<'input>>,
/// A map from identifiers in scope to the locals/arguments they represent. /// /// The handles refer to the [`locals`] arena; see that field's /// documentation for details. /// /// [`locals`]: ExpressionContext::locals
local_table: &'temp mut SymbolTable<&'input str, Handle<ast::Local>>,
/// Local variable and function argument arena for the function we're building. /// /// Note that the [`ast::Local`] here is actually a zero-sized type. This /// `Arena`'s only role is to assign a unique `Handle` to each local /// identifier, and track its definition's span for use in diagnostics. All /// the detailed information about locals - names, types, etc. - is kept in /// the [`LocalDecl`] statements we parsed from their declarations. For /// arguments, that information is kept in [`arguments`]. /// /// In the AST, when an [`Ident`] expression refers to a local variable or /// argument, its [`IdentExpr`] holds the referent's `Handle<Local>` in this /// arena. /// /// During lowering, [`LocalDecl`] statements add entries to a per-function /// table that maps `Handle<Local>` values to their Naga representations, /// accessed via [`StatementContext::local_table`] and /// [`LocalExpressionContext::local_table`]. This table is then consulted when /// lowering subsequent [`Ident`] expressions. /// /// [`LocalDecl`]: ast::StatementKind::LocalDecl /// [`arguments`]: ast::Function::arguments /// [`Ident`]: ast::Expression::Ident /// [`IdentExpr`]: ast::IdentExpr /// [`StatementContext::local_table`]: super::lower::StatementContext::local_table /// [`LocalExpressionContext::local_table`]: super::lower::LocalExpressionContext::local_table
locals: &'out mut Arena<ast::Local>,
/// Identifiers used by the current global declaration that have no local definition. /// /// This becomes the [`GlobalDecl`]'s [`dependencies`] set. /// /// Note that we don't know at parse time what kind of [`GlobalDecl`] the /// name refers to. We can't look up names until we've seen the entire /// translation unit. /// /// [`GlobalDecl`]: ast::GlobalDecl /// [`dependencies`]: ast::GlobalDecl::dependencies
unresolved: &'out mut FastIndexSet<ast::Dependency<'input>>,
}
/// Which grammar rule we are in the midst of parsing. /// /// This is used for error checking. `Parser` maintains a stack of /// these and (occasionally) checks that it is being pushed and popped /// as expected. #[derive(Copy, Clone, Debug, PartialEq)] enum Rule {
Attribute,
VariableDecl,
FunctionDecl,
Block,
Statement,
PrimaryExpr,
SingularExpr,
UnaryExpr,
GeneralExpr,
Directive,
GenericExpr,
EnclosedExpr,
LhsExpr,
}
loop { let expression = match lexer.peek().0 {
Token::Separator('.') => { let _ = lexer.next(); let field = lexer.next_ident()?;
ast::Expression::Member { base: expr, field }
}
Token::Paren('[') => { let _ = lexer.next(); let index = self.enclosed_expression(lexer, ctx)?;
lexer.expect(Token::Paren(']'))?;
lexer.expect(Token::Paren('{'))?; letmut ready = true; while !lexer.next_if(Token::Paren('}')) { if !ready { return Err(Box::new(Error::Unexpected(
lexer.next().1,
ExpectedToken::Token(Token::Separator(',')),
)));
}
let doc_comments = lexer.accumulate_doc_comments();
let (mut size, mut align) = (ParsedAttribute::default(), ParsedAttribute::default()); self.push_rule_span(Rule::Attribute, lexer); letmut bind_parser = BindingParser::default(); while lexer.next_if(Token::Attribute) { match lexer.next_ident_with_span()? {
("size", name_span) => {
lexer.expect(Token::Paren('('))?; let expr = self.expression(lexer, ctx)?;
lexer.next_if(Token::Separator(','));
lexer.expect(Token::Paren(')'))?;
size.set(expr, name_span)?;
}
("align", name_span) => {
lexer.expect(Token::Paren('('))?; let expr = self.expression(lexer, ctx)?;
lexer.next_if(Token::Separator(','));
lexer.expect(Token::Paren(')'))?;
align.set(expr, name_span)?;
}
(word, word_span) => bind_parser.parse(self, lexer, word, word_span, ctx)?,
}
}
let bind_span = self.pop_rule_span(lexer); let binding = bind_parser.finish(bind_span)?;
let name = lexer.next_ident()?;
lexer.expect(Token::Separator(':'))?; let ty = self.type_specifier(lexer, ctx)?;
ready = lexer.next_if(Token::Separator(','));
let span = lexer.span_with_start(token.1);
block.stmts.push(ast::Statement {
kind: ast::StatementKind::Assign { target, op, value },
span,
});
Ok(())
}
/// Parse a function call statement. /// /// This assumes that `token` has been consumed from the lexer. /// /// This does not consume or require a final `;` token. In the update /// expression of a C-style `for` loop header, there is no terminating `;`. fn maybe_func_call_statement<'a>(
&mutself,
lexer: &mut Lexer<'a>,
context: &mut ExpressionContext<'a, '_, '_>,
block: &mut ast::Block<'a>,
token: TokenSpan<'a>,
) -> Result<'a, bool> { let (name, name_span) = match token {
(Token::Word(name), span) => (name, span),
_ => return Ok(false),
}; let ident = self.template_elaborated_ident(name, name_span, lexer, context)?; if ident.template_list.is_empty() && !matches!(lexer.peek(), (Token::Paren('('), _)) { return Ok(false);
}
self.push_rule_span(Rule::SingularExpr, lexer);
let arguments = self.arguments(lexer, context)?; let span = lexer.span_with_start(name_span);
/// Parses func_call_statement and variable_updating_statement /// /// This does not consume or require a final `;` token. In the update /// expression of a C-style `for` loop header, there is no terminating `;`. fn func_call_or_variable_updating_statement<'a>(
&mutself,
lexer: &mut Lexer<'a>,
context: &mut ExpressionContext<'a, '_, '_>,
block: &mut ast::Block<'a>,
token: TokenSpan<'a>,
expected_token: ExpectedToken<'a>,
) -> Result<'a, ()> { if !self.maybe_func_call_statement(lexer, context, block, token)? { self.variable_updating_statement(lexer, context, block, token, expected_token)?;
}
Ok(())
}
/// Parses variable_or_value_statement, func_call_statement and variable_updating_statement. /// /// This is equivalent to the `for_init` production in the WGSL spec, /// but it's also used for parsing these forms when they appear within a block, /// hence the longer name. /// /// This does not consume the following `;` token. fn variable_or_value_or_func_call_or_variable_updating_statement<'a>(
&mutself,
lexer: &mut Lexer<'a>,
ctx: &mut ExpressionContext<'a, '_, '_>,
block: &mut ast::Block<'a>,
token: TokenSpan<'a>,
expected_token: ExpectedToken<'a>,
) -> Result<'a, ()> { let local_decl = match token {
(Token::Word("let"), _) => { let (name, given_ty) = self.optionally_typed_ident(lexer, ctx)?;
lexer.expect(Token::Operation('='))?; let expr_id = self.expression(lexer, ctx)?;
// We peek here instead of eagerly getting the next token since // `Parser::block` expects its first token to be `{`. // // Most callers have a single path leading to the start of the block; // `statement` is the only exception where there are multiple choices. match lexer.peek() {
(token, _) if is_start_of_compound_statement(token) => { let (inner, span) = this.block(lexer, ctx, brace_nesting_level)?;
block.stmts.push(ast::Statement {
kind: ast::StatementKind::Block(inner),
span,
});
this.pop_rule_span(lexer); return Ok(());
}
_ => {}
}
let kind = match lexer.next() {
(Token::Separator(';'), _) => {
this.pop_rule_span(lexer); return Ok(());
}
(Token::Word("return"), _) => { let value = if lexer.peek().0 != Token::Separator(';') { let handle = this.expression(lexer, ctx)?;
Some(handle)
} else {
None
};
lexer.expect(Token::Separator(';'))?;
ast::StatementKind::Return { value }
}
(Token::Word("if"), _) => { let condition = this.expression(lexer, ctx)?;
let accept = this.block(lexer, ctx, brace_nesting_level)?.0;
// ... else if (...) { ... } let other_condition = this.expression(lexer, ctx)?; let other_block = this.block(lexer, ctx, brace_nesting_level)?;
elsif_stack.push((elseif_span_start, other_condition, other_block));
elseif_span_start = lexer.start_byte_offset();
};
// reverse-fold the else-if blocks //Note: we may consider uplifting this to the IR for (other_span_start, other_cond, other_block) in elsif_stack.into_iter().rev()
{ let sub_stmt = ast::StatementKind::If {
condition: other_cond,
accept: other_block.0,
reject,
};
reject = ast::Block::default(); let span = lexer.span_from(other_span_start);
reject.stmts.push(ast::Statement {
kind: sub_stmt,
span,
})
}
ast::StatementKind::If {
condition,
accept,
reject,
}
}
(Token::Word("switch"), _) => { let selector = this.expression(lexer, ctx)?; let brace_span = lexer.expect_span(Token::Paren('{'))?; let brace_nesting_level = Self::increase_brace_nesting(brace_nesting_level, brace_span)?; letmut cases = Vec::new();
loop { // cases + default match lexer.next() {
(Token::Word("case"), _) => { // parse a list of values let value = loop { let value = this.switch_value(lexer, ctx)?; if lexer.next_if(Token::Separator(',')) { // list of values ends with ':' or a compound statement let next_token = lexer.peek().0; if next_token == Token::Separator(':')
|| is_start_of_compound_statement(next_token)
{ break value;
}
} else { break value;
}
cases.push(ast::SwitchCase {
value,
body: ast::Block::default(),
fall_through: true,
});
};
lexer.next_if(Token::Separator(':'));
let body = this.block(lexer, ctx, brace_nesting_level)?.0;
ast::StatementKind::Loop {
body,
continuing,
break_if: None,
}
}
(Token::Word("break"), span) => { // Check if the next token is an `if`, this indicates // that the user tried to type out a `break if` which // is illegal in this position. let (peeked_token, peeked_span) = lexer.peek(); iflet Token::Word("if") = peeked_token { let span = span.until(&peeked_span); return Err(Box::new(Error::InvalidBreakIf(span)));
}
lexer.expect(Token::Separator(';'))?;
ast::StatementKind::Break
}
(Token::Word("continue"), _) => {
lexer.expect(Token::Separator(';'))?;
ast::StatementKind::Continue
}
(Token::Word("discard"), _) => {
lexer.expect(Token::Separator(';'))?;
ast::StatementKind::Kill
} // https://www.w3.org/TR/WGSL/#const-assert-statement
(Token::Word("const_assert"), _) => { // parentheses are optional let paren = lexer.next_if(Token::Paren('('));
let brace_span = lexer.expect_span(Token::Paren('{'))?; let brace_nesting_level = Self::increase_brace_nesting(brace_nesting_level, brace_span)?;
ctx.local_table.push_scope();
loop { if lexer.next_if(Token::Word("continuing")) { // Branch for the `continuing` block, this must be // the last thing in the loop body
// Expect a opening brace to start the continuing block let brace_span = lexer.expect_span(Token::Paren('{'))?; let brace_nesting_level = Self::increase_brace_nesting(brace_nesting_level, brace_span)?; loop { if lexer.next_if(Token::Word("break")) { // Branch for the `break if` statement, this statement // has the form `break if <expr>;` and must be the last // statement in a continuing block
// The break must be followed by an `if` to form // the break if
lexer.expect(Token::Word("if"))?;
let condition = self.expression(lexer, ctx)?; // Set the condition of the break if to the newly parsed // expression
break_if = Some(condition);
// Expect a semicolon to close the statement
lexer.expect(Token::Separator(';'))?; // Expect a closing brace to close the continuing block, // since the break if must be the last statement
lexer.expect(Token::Paren('}'))?; // Stop parsing the continuing block break;
} elseif lexer.next_if(Token::Paren('}')) { // If we encounter a closing brace it means we have reached // the end of the continuing block and should stop processing break;
} else { // Otherwise try to parse a statement self.statement(lexer, ctx, &mut continuing, brace_nesting_level)?;
}
} // Since the continuing block must be the last part of the loop body, // we expect to see a closing brace to end the loop body
lexer.expect(Token::Paren('}'))?; break;
} if lexer.next_if(Token::Paren('}')) { // If we encounter a closing brace it means we have reached // the end of the loop body and should stop processing break;
} // Otherwise try to parse a statement self.statement(lexer, ctx, &mut body, brace_nesting_level)?;
}
// start a scope that contains arguments as well as the function body
ctx.local_table.push_scope(); // Reduce lookup scope to parse the parameter list and return type // avoiding identifier lookup to match newly declared param names.
ctx.local_table.reduce_lookup_scope();
// read parameter list letmut arguments = Vec::new();
lexer.expect(Token::Paren('('))?; letmut ready = true; while !lexer.next_if(Token::Paren(')')) { if !ready { return Err(Box::new(Error::Unexpected(
lexer.next().1,
ExpectedToken::Token(Token::Separator(',')),
)));
} let binding = self.varying_binding(lexer, &mut ctx)?;
let param_name = lexer.next_ident()?;
lexer.expect(Token::Separator(':'))?; let param_type = self.type_specifier(lexer, &mut ctx)?;
let handle = ctx.declare_local(param_name)?;
arguments.push(ast::FunctionArgument {
name: param_name,
ty: param_type,
binding,
handle,
});
ready = lexer.next_if(Token::Separator(','));
} // read return type let result = if lexer.next_if(Token::Arrow) { let binding = self.varying_binding(lexer, &mut ctx)?; let ty = self.type_specifier(lexer, &mut ctx)?; let must_use = must_use.is_some();
Some(ast::FunctionResult {
ty,
binding,
must_use,
})
} elseiflet Some(must_use) = must_use { return Err(Box::new(Error::FunctionMustUseReturnsVoid(
must_use, self.peek_rule_span(lexer),
)));
} else {
None
};
ctx.local_table.reset_lookup_scope();
// do not use `self.block` here, since we must not push a new scope
lexer.expect(Token::Paren('{'))?; let brace_nesting_level = 1; letmut body = ast::Block::default(); while !lexer.next_if(Token::Paren('}')) { self.statement(lexer, &mut ctx, &mut body, brace_nesting_level)?;
}
ctx.local_table.pop_scope();
let fun = ast::Function {
entry_point: None,
name: fun_name,
arguments,
result,
body,
diagnostic_filter_leaf,
doc_comments: Vec::new(),
};
// read attributes letmut binding = None; letmut stage = ParsedAttribute::default(); // Span in case we need to report an error for a shader stage missing something (e.g. its workgroup size). // Doesn't need to be set in the vertex and fragment stages because they don't have errors like that. letmut shader_stage_error_span = Span::new(0, 0); letmut workgroup_size = ParsedAttribute::default(); letmut early_depth_test = ParsedAttribute::default(); let (mut bind_index, mut bind_group) =
(ParsedAttribute::default(), ParsedAttribute::default()); letmut id = ParsedAttribute::default(); // the payload variable for a mesh shader letmut payload = ParsedAttribute::default(); // the incoming payload from a traceRay call letmut incoming_payload = ParsedAttribute::default(); letmut mesh_output = ParsedAttribute::default();
if !self.rules.is_empty() {
log::error!("Reached the end of global decl, but rule stack is not empty");
log::error!("Rules: {:?}", self.rules); return Err(Box::new(Error::Internal("rule stack is not empty")));
};
match binding {
None => Ok(()),
Some(_) => Err(Box::new(Error::Internal( "we had the attribute but no var?",
))),
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.