duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

effect_parser_stmt.cpp (74727B)


      1 /*
      2  * Copyright (C) 2014 Patrick Mours
      3  * SPDX-License-Identifier: BSD-3-Clause
      4  */
      5 
      6 #include "effect_lexer.hpp"
      7 #include "effect_parser.hpp"
      8 #include "effect_codegen.hpp"
      9 #include <cctype> // std::toupper
     10 #include <cassert>
     11 #include <limits>
     12 #include <functional>
     13 #include <string_view>
     14 
     15 struct on_scope_exit
     16 {
     17 	template <typename F>
     18 	explicit on_scope_exit(F lambda) : leave(lambda) { }
     19 	~on_scope_exit() { leave(); }
     20 
     21 	std::function<void()> leave;
     22 };
     23 
     24 bool reshadefx::parser::parse(std::string input, codegen *backend)
     25 {
     26 	_lexer.reset(new lexer(std::move(input)));
     27 
     28 	// Set backend for subsequent code-generation
     29 	_codegen = backend;
     30 	assert(backend != nullptr);
     31 
     32 	consume();
     33 
     34 	bool parse_success = true;
     35 	bool current_success = true;
     36 
     37 	while (!peek(tokenid::end_of_file))
     38 	{
     39 		parse_top(current_success);
     40 		if (!current_success)
     41 			parse_success = false;
     42 	}
     43 
     44 	return parse_success;
     45 }
     46 
     47 void reshadefx::parser::parse_top(bool &parse_success)
     48 {
     49 	if (accept(tokenid::namespace_))
     50 	{
     51 		// Anonymous namespaces are not supported right now, so an identifier is a must
     52 		if (!expect(tokenid::identifier))
     53 		{
     54 			parse_success = false;
     55 			return;
     56 		}
     57 
     58 		const auto name = std::move(_token.literal_as_string);
     59 
     60 		if (!expect('{'))
     61 		{
     62 			parse_success = false;
     63 			return;
     64 		}
     65 
     66 		enter_namespace(name);
     67 
     68 		bool current_success = true;
     69 		bool parse_success_namespace = true;
     70 
     71 		// Recursively parse top level statements until the namespace is closed again
     72 		while (!peek('}')) // Empty namespaces are valid
     73 		{
     74 			parse_top(current_success);
     75 			if (!current_success)
     76 				parse_success_namespace = false;
     77 		}
     78 
     79 		leave_namespace();
     80 
     81 		parse_success = expect('}') && parse_success_namespace;
     82 	}
     83 	else if (accept(tokenid::struct_)) // Structure keyword found, parse the structure definition
     84 	{
     85 		// Structure definitions are terminated with a semicolon
     86 		parse_success = parse_struct() && expect(';');
     87 	}
     88 	else if (accept(tokenid::technique)) // Technique keyword found, parse the technique definition
     89 	{
     90 		parse_success = parse_technique();
     91 	}
     92 	else
     93 	{
     94 		if (type type; parse_type(type)) // Type found, this can be either a variable or a function declaration
     95 		{
     96 			parse_success = expect(tokenid::identifier);
     97 			if (!parse_success)
     98 				return;
     99 
    100 			if (peek('('))
    101 			{
    102 				const auto name = std::move(_token.literal_as_string);
    103 				// This is definitely a function declaration, so parse it
    104 				if (!parse_function(type, name))
    105 				{
    106 					// Insert dummy function into symbol table, so later references can be resolved despite the error
    107 					insert_symbol(name, { symbol_type::function, ~0u, { type::t_function } }, true);
    108 					parse_success = false;
    109 					return;
    110 				}
    111 			}
    112 			else
    113 			{
    114 				// There may be multiple variable names after the type, handle them all
    115 				unsigned int count = 0;
    116 				do {
    117 					if (count++ > 0 && !(expect(',') && expect(tokenid::identifier)))
    118 					{
    119 						parse_success = false;
    120 						return;
    121 					}
    122 					const auto name = std::move(_token.literal_as_string);
    123 					if (!parse_variable(type, name, true))
    124 					{
    125 						// Insert dummy variable into symbol table, so later references can be resolved despite the error
    126 						insert_symbol(name, { symbol_type::variable, ~0u, type }, true);
    127 						// Skip the rest of the statement
    128 						consume_until(';');
    129 						parse_success = false;
    130 						return;
    131 					}
    132 				} while (!peek(';'));
    133 
    134 				// Variable declarations are terminated with a semicolon
    135 				parse_success = expect(';');
    136 			}
    137 		}
    138 		else if (accept(';')) // Ignore single semicolons in the source
    139 		{
    140 			parse_success = true;
    141 		}
    142 		else
    143 		{
    144 			// Unexpected token in source stream, consume and report an error about it
    145 			consume();
    146 			// Only add another error message if succeeded parsing previously
    147 			// This is done to avoid walls of error messages because of consequential errors following a top-level syntax mistake
    148 			if (parse_success)
    149 				error(_token.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token.id) + '\'');
    150 			parse_success = false;
    151 		}
    152 	}
    153 }
    154 
    155 bool reshadefx::parser::parse_statement(bool scoped)
    156 {
    157 	if (!_codegen->is_in_block())
    158 		return error(_token_next.location, 0, "unreachable code"), false;
    159 
    160 	unsigned int loop_control = 0;
    161 	unsigned int selection_control = 0;
    162 
    163 	// Read any loop and branch control attributes first
    164 	while (accept('['))
    165 	{
    166 		enum control_mask
    167 		{
    168 			unroll = 0x1,
    169 			dont_unroll = 0x2,
    170 			flatten = (0x1 << 4),
    171 			dont_flatten = (0x2 << 4),
    172 			switch_force_case = (0x4 << 4),
    173 			switch_call = (0x8 << 4)
    174 		};
    175 
    176 		const auto attribute = std::move(_token_next.literal_as_string);
    177 
    178 		if (!expect(tokenid::identifier) || !expect(']'))
    179 			return false;
    180 
    181 		if (attribute == "unroll")
    182 			loop_control |= unroll;
    183 		else if (attribute == "loop" || attribute == "fastopt")
    184 			loop_control |= dont_unroll;
    185 		else if (attribute == "flatten")
    186 			selection_control |= flatten;
    187 		else if (attribute == "branch")
    188 			selection_control |= dont_flatten;
    189 		else if (attribute == "forcecase")
    190 			selection_control |= switch_force_case;
    191 		else if (attribute == "call")
    192 			selection_control |= switch_call;
    193 		else
    194 			warning(_token.location, 0, "unknown attribute");
    195 
    196 		if ((loop_control & (unroll | dont_unroll)) == (unroll | dont_unroll))
    197 			return error(_token.location, 3524, "can't use loop and unroll attributes together"), false;
    198 		if ((selection_control & (flatten | dont_flatten)) == (flatten | dont_flatten))
    199 			return error(_token.location, 3524, "can't use branch and flatten attributes together"), false;
    200 	}
    201 
    202 	// Shift by two so that the possible values are 0x01 for 'flatten' and 0x02 for 'dont_flatten', equivalent to 'unroll' and 'dont_unroll'
    203 	selection_control >>= 4;
    204 
    205 	if (peek('{')) // Parse statement block
    206 		return parse_statement_block(scoped);
    207 	else if (accept(';')) // Ignore empty statements
    208 		return true;
    209 
    210 	// Most statements with the exception of declarations are only valid inside functions
    211 	if (_codegen->is_in_function())
    212 	{
    213 		assert(_current_function != nullptr);
    214 
    215 		const auto location = _token_next.location;
    216 
    217 		if (accept(tokenid::if_))
    218 		{
    219 			codegen::id true_block = _codegen->create_block(); // Block which contains the statements executed when the condition is true
    220 			codegen::id false_block = _codegen->create_block(); // Block which contains the statements executed when the condition is false
    221 			const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the branch re-merged with the current control flow
    222 
    223 			expression condition;
    224 			if (!expect('(') || !parse_expression(condition) || !expect(')'))
    225 				return false;
    226 			else if (!condition.type.is_scalar())
    227 				return error(condition.location, 3019, "if statement conditional expressions must evaluate to a scalar"), false;
    228 
    229 			// Load condition and convert to boolean value as required by 'OpBranchConditional'
    230 			condition.add_cast_operation({ type::t_bool, 1, 1 });
    231 
    232 			const codegen::id condition_value = _codegen->emit_load(condition);
    233 			const codegen::id condition_block = _codegen->leave_block_and_branch_conditional(condition_value, true_block, false_block);
    234 
    235 			{ // Then block of the if statement
    236 				_codegen->enter_block(true_block);
    237 
    238 				if (!parse_statement(true))
    239 					return false;
    240 
    241 				true_block = _codegen->leave_block_and_branch(merge_block);
    242 			}
    243 			{ // Else block of the if statement
    244 				_codegen->enter_block(false_block);
    245 
    246 				if (accept(tokenid::else_) && !parse_statement(true))
    247 					return false;
    248 
    249 				false_block = _codegen->leave_block_and_branch(merge_block);
    250 			}
    251 
    252 			_codegen->enter_block(merge_block);
    253 
    254 			// Emit structured control flow for an if statement and connect all basic blocks
    255 			_codegen->emit_if(location, condition_value, condition_block, true_block, false_block, selection_control);
    256 
    257 			return true;
    258 		}
    259 
    260 		if (accept(tokenid::switch_))
    261 		{
    262 			const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the switch re-merged with the current control flow
    263 
    264 			expression selector_exp;
    265 			if (!expect('(') || !parse_expression(selector_exp) || !expect(')'))
    266 				return false;
    267 			else if (!selector_exp.type.is_scalar())
    268 				return error(selector_exp.location, 3019, "switch statement expression must evaluate to a scalar"), false;
    269 
    270 			// Load selector and convert to integral value as required by switch instruction
    271 			selector_exp.add_cast_operation({ type::t_int, 1, 1 });
    272 
    273 			const auto selector_value = _codegen->emit_load(selector_exp);
    274 			const auto selector_block = _codegen->leave_block_and_switch(selector_value, merge_block);
    275 
    276 			if (!expect('{'))
    277 				return false;
    278 
    279 			_loop_break_target_stack.push_back(merge_block);
    280 			on_scope_exit _([this]() { _loop_break_target_stack.pop_back(); });
    281 
    282 			bool parse_success = true;
    283 			// The default case jumps to the end of the switch statement if not overwritten
    284 			codegen::id default_label = merge_block, default_block = merge_block;
    285 			codegen::id current_label = _codegen->create_block();
    286 			std::vector<codegen::id> case_literal_and_labels, case_blocks;
    287 			size_t last_case_label_index = 0;
    288 
    289 			// Enter first switch statement body block
    290 			_codegen->enter_block(current_label);
    291 
    292 			while (!peek(tokenid::end_of_file))
    293 			{
    294 				while (accept(tokenid::case_) || accept(tokenid::default_))
    295 				{
    296 					if (_token.id == tokenid::case_)
    297 					{
    298 						expression case_label;
    299 						if (!parse_expression(case_label))
    300 							return consume_until('}'), false;
    301 						else if (!case_label.type.is_scalar() || !case_label.type.is_integral() || !case_label.is_constant)
    302 							return error(case_label.location, 3020, "invalid type for case expression - value must be an integer scalar"), consume_until('}'), false;
    303 
    304 						// Check for duplicate case values
    305 						for (size_t i = 0; i < case_literal_and_labels.size(); i += 2)
    306 						{
    307 							if (case_literal_and_labels[i] == case_label.constant.as_uint[0])
    308 							{
    309 								parse_success = false;
    310 								error(case_label.location, 3532, "duplicate case " + std::to_string(case_label.constant.as_uint[0]));
    311 								break;
    312 							}
    313 						}
    314 
    315 						case_blocks.emplace_back(); // This is set to the actual block below
    316 						case_literal_and_labels.push_back(case_label.constant.as_uint[0]);
    317 						case_literal_and_labels.push_back(current_label);
    318 					}
    319 					else
    320 					{
    321 						// Check if the default label was already changed by a previous 'default' statement
    322 						if (default_label != merge_block)
    323 						{
    324 							parse_success = false;
    325 							error(_token.location, 3532, "duplicate default in switch statement");
    326 						}
    327 
    328 						default_label = current_label;
    329 						default_block = 0; // This is set to the actual block below
    330 					}
    331 
    332 					if (!expect(':'))
    333 						return consume_until('}'), false;
    334 				}
    335 
    336 				// It is valid for no statement to follow if this is the last label in the switch body
    337 				const bool end_of_switch = peek('}');
    338 
    339 				if (!end_of_switch && !parse_statement(true))
    340 					return consume_until('}'), false;
    341 
    342 				// Handle fall-through case and end of switch statement
    343 				if (peek(tokenid::case_) || peek(tokenid::default_) || end_of_switch)
    344 				{
    345 					if (_codegen->is_in_block()) // Disallow fall-through for now
    346 					{
    347 						parse_success = false;
    348 						error(_token_next.location, 3533, "non-empty case statements must have break or return");
    349 					}
    350 
    351 					const codegen::id next_label = end_of_switch ? merge_block : _codegen->create_block();
    352 					// This is different from 'current_label', since there may have been branching logic inside the case, which would have changed the active block
    353 					const codegen::id current_block = _codegen->leave_block_and_branch(next_label);
    354 
    355 					if (0 == default_block)
    356 						default_block = current_block;
    357 					for (size_t i = last_case_label_index; i < case_blocks.size(); ++i)
    358 						// Need to use the initial label for the switch table, but the current block to merge all the block data
    359 						case_blocks[i] = current_block;
    360 
    361 					current_label = next_label;
    362 					_codegen->enter_block(current_label);
    363 
    364 					if (end_of_switch) // We reached the end, nothing more to do
    365 						break;
    366 
    367 					last_case_label_index = case_blocks.size();
    368 				}
    369 			}
    370 
    371 			if (case_literal_and_labels.empty() && default_label == merge_block)
    372 				warning(location, 5002, "switch statement contains no 'case' or 'default' labels");
    373 
    374 			// Emit structured control flow for a switch statement and connect all basic blocks
    375 			_codegen->emit_switch(location, selector_value, selector_block, default_label, default_block, case_literal_and_labels, case_blocks, selection_control);
    376 
    377 			return expect('}') && parse_success;
    378 		}
    379 
    380 		if (accept(tokenid::for_))
    381 		{
    382 			if (!expect('('))
    383 				return false;
    384 
    385 			enter_scope();
    386 			on_scope_exit _([this]() { leave_scope(); });
    387 
    388 			// Parse initializer first
    389 			if (type type; parse_type(type))
    390 			{
    391 				unsigned int count = 0;
    392 				do { // There may be multiple declarations behind a type, so loop through them
    393 					if (count++ > 0 && !expect(','))
    394 						return false;
    395 					if (!expect(tokenid::identifier) || !parse_variable(type, std::move(_token.literal_as_string)))
    396 						return false;
    397 				} while (!peek(';'));
    398 			}
    399 			else
    400 			{
    401 				// Initializer can also contain an expression if not a variable declaration list and not empty
    402 				if (!peek(';'))
    403 				{
    404 					expression expression;
    405 					if (!parse_expression(expression))
    406 						return false;
    407 				}
    408 			}
    409 
    410 			if (!expect(';'))
    411 				return false;
    412 
    413 			const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the loop
    414 			const codegen::id header_label = _codegen->create_block(); // Pointer to the loop merge instruction
    415 			const codegen::id continue_label = _codegen->create_block(); // Pointer to the continue block
    416 			codegen::id loop_block = _codegen->create_block(); // Pointer to the main loop body block
    417 			codegen::id condition_block = _codegen->create_block(); // Pointer to the condition check
    418 			codegen::id condition_value = 0;
    419 
    420 			// End current block by branching to the next label
    421 			const codegen::id prev_block = _codegen->leave_block_and_branch(header_label);
    422 
    423 			{ // Begin loop block (this header is used for explicit structured control flow)
    424 				_codegen->enter_block(header_label);
    425 
    426 				_codegen->leave_block_and_branch(condition_block);
    427 			}
    428 
    429 			{ // Parse condition block
    430 				_codegen->enter_block(condition_block);
    431 
    432 				if (!peek(';'))
    433 				{
    434 					expression condition;
    435 					if (!parse_expression(condition))
    436 						return false;
    437 
    438 					if (!condition.type.is_scalar())
    439 						return error(condition.location, 3019, "scalar value expected"), false;
    440 
    441 					// Evaluate condition and branch to the right target
    442 					condition.add_cast_operation({ type::t_bool, 1, 1 });
    443 
    444 					condition_value = _codegen->emit_load(condition);
    445 
    446 					condition_block = _codegen->leave_block_and_branch_conditional(condition_value, loop_block, merge_block);
    447 				}
    448 				else // It is valid for there to be no condition expression
    449 				{
    450 					condition_block = _codegen->leave_block_and_branch(loop_block);
    451 				}
    452 
    453 				if (!expect(';'))
    454 					return false;
    455 			}
    456 
    457 			{ // Parse loop continue block into separate block so it can be appended to the end down the line
    458 				_codegen->enter_block(continue_label);
    459 
    460 				if (!peek(')'))
    461 				{
    462 					expression continue_exp;
    463 					if (!parse_expression(continue_exp))
    464 						return false;
    465 				}
    466 
    467 				if (!expect(')'))
    468 					return false;
    469 
    470 				// Branch back to the loop header at the end of the continue block
    471 				_codegen->leave_block_and_branch(header_label);
    472 			}
    473 
    474 			{ // Parse loop body block
    475 				_codegen->enter_block(loop_block);
    476 
    477 				_loop_break_target_stack.push_back(merge_block);
    478 				_loop_continue_target_stack.push_back(continue_label);
    479 
    480 				const bool parse_success = parse_statement(false);
    481 
    482 				_loop_break_target_stack.pop_back();
    483 				_loop_continue_target_stack.pop_back();
    484 
    485 				if (!parse_success)
    486 					return false;
    487 
    488 				loop_block = _codegen->leave_block_and_branch(continue_label);
    489 			}
    490 
    491 			// Add merge block label to the end of the loop
    492 			_codegen->enter_block(merge_block);
    493 
    494 			// Emit structured control flow for a loop statement and connect all basic blocks
    495 			_codegen->emit_loop(location, condition_value, prev_block, header_label, condition_block, loop_block, continue_label, loop_control);
    496 
    497 			return true;
    498 		}
    499 
    500 		if (accept(tokenid::while_))
    501 		{
    502 			enter_scope();
    503 			on_scope_exit _([this]() { leave_scope(); });
    504 
    505 			const codegen::id merge_block = _codegen->create_block();
    506 			const codegen::id header_label = _codegen->create_block();
    507 			const codegen::id continue_label = _codegen->create_block();
    508 			codegen::id loop_block = _codegen->create_block();
    509 			codegen::id condition_block = _codegen->create_block();
    510 			codegen::id condition_value = 0;
    511 
    512 			// End current block by branching to the next label
    513 			const codegen::id prev_block = _codegen->leave_block_and_branch(header_label);
    514 
    515 			{ // Begin loop block
    516 				_codegen->enter_block(header_label);
    517 
    518 				_codegen->leave_block_and_branch(condition_block);
    519 			}
    520 
    521 			{ // Parse condition block
    522 				_codegen->enter_block(condition_block);
    523 
    524 				expression condition;
    525 				if (!expect('(') || !parse_expression(condition) || !expect(')'))
    526 					return false;
    527 				else if (!condition.type.is_scalar())
    528 					return error(condition.location, 3019, "scalar value expected"), false;
    529 
    530 				// Evaluate condition and branch to the right target
    531 				condition.add_cast_operation({ type::t_bool, 1, 1 });
    532 
    533 				condition_value = _codegen->emit_load(condition);
    534 
    535 				condition_block = _codegen->leave_block_and_branch_conditional(condition_value, loop_block, merge_block);
    536 			}
    537 
    538 			{ // Parse loop body block
    539 				_codegen->enter_block(loop_block);
    540 
    541 				_loop_break_target_stack.push_back(merge_block);
    542 				_loop_continue_target_stack.push_back(continue_label);
    543 
    544 				const bool parse_success = parse_statement(false);
    545 
    546 				_loop_break_target_stack.pop_back();
    547 				_loop_continue_target_stack.pop_back();
    548 
    549 				if (!parse_success)
    550 					return false;
    551 
    552 				loop_block = _codegen->leave_block_and_branch(continue_label);
    553 			}
    554 
    555 			{ // Branch back to the loop header in empty continue block
    556 				_codegen->enter_block(continue_label);
    557 
    558 				_codegen->leave_block_and_branch(header_label);
    559 			}
    560 
    561 			// Add merge block label to the end of the loop
    562 			_codegen->enter_block(merge_block);
    563 
    564 			// Emit structured control flow for a loop statement and connect all basic blocks
    565 			_codegen->emit_loop(location, condition_value, prev_block, header_label, condition_block, loop_block, continue_label, loop_control);
    566 
    567 			return true;
    568 		}
    569 
    570 		if (accept(tokenid::do_))
    571 		{
    572 			const codegen::id merge_block = _codegen->create_block();
    573 			const codegen::id header_label = _codegen->create_block();
    574 			const codegen::id continue_label = _codegen->create_block();
    575 			codegen::id loop_block = _codegen->create_block();
    576 			codegen::id condition_value = 0;
    577 
    578 			// End current block by branching to the next label
    579 			const codegen::id prev_block = _codegen->leave_block_and_branch(header_label);
    580 
    581 			{ // Begin loop block
    582 				_codegen->enter_block(header_label);
    583 
    584 				_codegen->leave_block_and_branch(loop_block);
    585 			}
    586 
    587 			{ // Parse loop body block
    588 				_codegen->enter_block(loop_block);
    589 
    590 				_loop_break_target_stack.push_back(merge_block);
    591 				_loop_continue_target_stack.push_back(continue_label);
    592 
    593 				const bool parse_success = parse_statement(true);
    594 
    595 				_loop_break_target_stack.pop_back();
    596 				_loop_continue_target_stack.pop_back();
    597 
    598 				if (!parse_success)
    599 					return false;
    600 
    601 				loop_block = _codegen->leave_block_and_branch(continue_label);
    602 			}
    603 
    604 			{ // Continue block does the condition evaluation
    605 				_codegen->enter_block(continue_label);
    606 
    607 				expression condition;
    608 				if (!expect(tokenid::while_) || !expect('(') || !parse_expression(condition) || !expect(')') || !expect(';'))
    609 					return false;
    610 				else if (!condition.type.is_scalar())
    611 					return error(condition.location, 3019, "scalar value expected"), false;
    612 
    613 				// Evaluate condition and branch to the right target
    614 				condition.add_cast_operation({ type::t_bool, 1, 1 });
    615 
    616 				condition_value = _codegen->emit_load(condition);
    617 
    618 				_codegen->leave_block_and_branch_conditional(condition_value, header_label, merge_block);
    619 			}
    620 
    621 			// Add merge block label to the end of the loop
    622 			_codegen->enter_block(merge_block);
    623 
    624 			// Emit structured control flow for a loop statement and connect all basic blocks
    625 			_codegen->emit_loop(location, condition_value, prev_block, header_label, 0, loop_block, continue_label, loop_control);
    626 
    627 			return true;
    628 		}
    629 
    630 		if (accept(tokenid::break_))
    631 		{
    632 			if (_loop_break_target_stack.empty())
    633 				return error(location, 3518, "break must be inside loop"), false;
    634 
    635 			// Branch to the break target of the inner most loop on the stack
    636 			_codegen->leave_block_and_branch(_loop_break_target_stack.back(), 1);
    637 
    638 			return expect(';');
    639 		}
    640 
    641 		if (accept(tokenid::continue_))
    642 		{
    643 			if (_loop_continue_target_stack.empty())
    644 				return error(location, 3519, "continue must be inside loop"), false;
    645 
    646 			// Branch to the continue target of the inner most loop on the stack
    647 			_codegen->leave_block_and_branch(_loop_continue_target_stack.back(), 2);
    648 
    649 			return expect(';');
    650 		}
    651 
    652 		if (accept(tokenid::return_))
    653 		{
    654 			const type &ret_type = _current_function->return_type;
    655 
    656 			if (!peek(';'))
    657 			{
    658 				expression expression;
    659 				if (!parse_expression(expression))
    660 					return consume_until(';'), false;
    661 
    662 				// Cannot return to void
    663 				if (ret_type.is_void())
    664 					// Consume the semicolon that follows the return expression so that parsing may continue
    665 					return error(location, 3079, "void functions cannot return a value"), accept(';'), false;
    666 
    667 				// Cannot return arrays from a function
    668 				if (expression.type.is_array() || !type::rank(expression.type, ret_type))
    669 					return error(location, 3017, "expression (" + expression.type.description() + ") does not match function return type (" + ret_type.description() + ')'), accept(';'), false;
    670 
    671 				// Load return value and perform implicit cast to function return type
    672 				if (expression.type.components() > ret_type.components())
    673 					warning(expression.location, 3206, "implicit truncation of vector type");
    674 
    675 				expression.add_cast_operation(ret_type);
    676 
    677 				const auto return_value = _codegen->emit_load(expression);
    678 
    679 				_codegen->leave_block_and_return(return_value);
    680 			}
    681 			else if (!ret_type.is_void())
    682 			{
    683 				// No return value was found, but the function expects one
    684 				error(location, 3080, "function must return a value");
    685 
    686 				// Consume the semicolon that follows the return expression so that parsing may continue
    687 				accept(';');
    688 
    689 				return false;
    690 			}
    691 			else
    692 			{
    693 				_codegen->leave_block_and_return();
    694 			}
    695 
    696 			return expect(';');
    697 		}
    698 
    699 		if (accept(tokenid::discard_))
    700 		{
    701 			// Leave the current function block
    702 			_codegen->leave_block_and_kill();
    703 
    704 			return expect(';');
    705 		}
    706 	}
    707 
    708 	// Handle variable declarations
    709 	if (type type; parse_type(type))
    710 	{
    711 		unsigned int count = 0;
    712 		do { // There may be multiple declarations behind a type, so loop through them
    713 			if (count++ > 0 && !expect(','))
    714 				// Try to consume the rest of the declaration so that parsing may continue despite the error
    715 				return consume_until(';'), false;
    716 			if (!expect(tokenid::identifier) || !parse_variable(type, std::move(_token.literal_as_string)))
    717 				return consume_until(';'), false;
    718 		} while (!peek(';'));
    719 
    720 		return expect(';');
    721 	}
    722 
    723 	// Handle expression statements
    724 	if (expression expression; parse_expression(expression))
    725 		return expect(';'); // A statement has to be terminated with a semicolon
    726 
    727 	// Gracefully consume any remaining characters until the statement would usually end, so that parsing may continue despite the error
    728 	consume_until(';');
    729 
    730 	return false;
    731 }
    732 bool reshadefx::parser::parse_statement_block(bool scoped)
    733 {
    734 	if (!expect('{'))
    735 		return false;
    736 
    737 	if (scoped)
    738 		enter_scope();
    739 
    740 	// Parse statements until the end of the block is reached
    741 	while (!peek('}') && !peek(tokenid::end_of_file))
    742 	{
    743 		if (!parse_statement(true))
    744 		{
    745 			if (scoped)
    746 				leave_scope();
    747 
    748 			// Ignore the rest of this block
    749 			unsigned int level = 0;
    750 
    751 			while (!peek(tokenid::end_of_file))
    752 			{
    753 				if (accept('{'))
    754 				{
    755 					++level;
    756 				}
    757 				else if (accept('}'))
    758 				{
    759 					if (level-- == 0)
    760 						break;
    761 				} // These braces are necessary to match the 'else' to the correct 'if' statement
    762 				else
    763 				{
    764 					consume();
    765 				}
    766 			}
    767 
    768 			return false;
    769 		}
    770 	}
    771 
    772 	if (scoped)
    773 		leave_scope();
    774 
    775 	return expect('}');
    776 }
    777 
    778 bool reshadefx::parser::parse_type(type &type)
    779 {
    780 	type.qualifiers = 0;
    781 
    782 	accept_type_qualifiers(type);
    783 
    784 	if (!accept_type_class(type))
    785 		return false;
    786 
    787 	if (type.is_integral() && (type.has(type::q_centroid) || type.has(type::q_noperspective)))
    788 		return error(_token.location, 4576, "signature specifies invalid interpolation mode for integer component type"), false;
    789 	else if (type.has(type::q_centroid) && !type.has(type::q_noperspective))
    790 		type.qualifiers |= type::q_linear;
    791 
    792 	return true;
    793 }
    794 bool reshadefx::parser::parse_array_size(type &type)
    795 {
    796 	// Reset array length to zero before checking if one exists
    797 	type.array_length = 0;
    798 
    799 	if (accept('['))
    800 	{
    801 		if (accept(']'))
    802 		{
    803 			// No length expression, so this is an unsized array
    804 			type.array_length = -1;
    805 		}
    806 		else if (expression expression; parse_expression(expression) && expect(']'))
    807 		{
    808 			if (!expression.is_constant || !(expression.type.is_scalar() && expression.type.is_integral()))
    809 				return error(expression.location, 3058, "array dimensions must be literal scalar expressions"), false;
    810 
    811 			type.array_length = expression.constant.as_uint[0];
    812 
    813 			if (type.array_length < 1 || type.array_length > 65536)
    814 				return error(expression.location, 3059, "array dimension must be between 1 and 65536"), false;
    815 		}
    816 		else
    817 		{
    818 			return false;
    819 		}
    820 	}
    821 
    822 	// Multi-dimensional arrays are not supported
    823 	if (peek('['))
    824 		return error(_token_next.location, 3119, "arrays cannot be multi-dimensional"), false;
    825 
    826 	return true;
    827 }
    828 
    829 bool reshadefx::parser::parse_annotations(std::vector<annotation> &annotations)
    830 {
    831 	// Check if annotations exist and return early if none do
    832 	if (!accept('<'))
    833 		return true;
    834 
    835 	bool parse_success = true;
    836 
    837 	while (!peek('>'))
    838 	{
    839 		if (type type; accept_type_class(type))
    840 			warning(_token.location, 4717, "type prefixes for annotations are deprecated and ignored");
    841 
    842 		if (!expect(tokenid::identifier))
    843 			return consume_until('>'), false;
    844 
    845 		auto name = std::move(_token.literal_as_string);
    846 
    847 		if (expression expression; !expect('=') || !parse_expression_multary(expression) || !expect(';'))
    848 			return consume_until('>'), false;
    849 		else if (expression.is_constant)
    850 			annotations.push_back({ expression.type, std::move(name), std::move(expression.constant) });
    851 		else // Continue parsing annotations despite this not being a constant, since the syntax is still correct
    852 			parse_success = false,
    853 			error(expression.location, 3011, "value must be a literal expression");
    854 	}
    855 
    856 	return expect('>') && parse_success;
    857 }
    858 
    859 bool reshadefx::parser::parse_struct()
    860 {
    861 	const auto location = std::move(_token.location);
    862 
    863 	struct_info info;
    864 	// The structure name is optional
    865 	if (accept(tokenid::identifier))
    866 		info.name = std::move(_token.literal_as_string);
    867 	else
    868 		info.name = "_anonymous_struct_" + std::to_string(location.line) + '_' + std::to_string(location.column);
    869 
    870 	info.unique_name = 'S' + current_scope().name + info.name;
    871 	std::replace(info.unique_name.begin(), info.unique_name.end(), ':', '_');
    872 
    873 	if (!expect('{'))
    874 		return false;
    875 
    876 	bool parse_success = true;
    877 
    878 	while (!peek('}')) // Empty structures are possible
    879 	{
    880 		struct_member_info member;
    881 
    882 		if (!parse_type(member.type))
    883 			return error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected struct member type"), consume_until('}'), accept(';'), false;
    884 
    885 		unsigned int count = 0;
    886 		do {
    887 			if (count++ > 0 && !expect(','))
    888 				return consume_until('}'), accept(';'), false;
    889 
    890 			if (!expect(tokenid::identifier))
    891 				return consume_until('}'), accept(';'), false;
    892 
    893 			member.name = std::move(_token.literal_as_string);
    894 			member.location = std::move(_token.location);
    895 
    896 			if (member.type.is_void())
    897 				parse_success = false,
    898 				error(member.location, 3038, '\'' + member.name + "': struct members cannot be void");
    899 			if (member.type.is_struct()) // Nesting structures would make input/output argument flattening more complicated, so prevent it for now
    900 				parse_success = false,
    901 				error(member.location, 3090, '\'' + member.name + "': nested struct members are not supported");
    902 
    903 			if (member.type.has(type::q_in) || member.type.has(type::q_out))
    904 				parse_success = false,
    905 				error(member.location, 3055, '\'' + member.name + "': struct members cannot be declared 'in' or 'out'");
    906 			if (member.type.has(type::q_const))
    907 				parse_success = false,
    908 				error(member.location, 3035, '\'' + member.name + "': struct members cannot be declared 'const'");
    909 			if (member.type.has(type::q_extern))
    910 				parse_success = false,
    911 				error(member.location, 3006, '\'' + member.name + "': struct members cannot be declared 'extern'");
    912 			if (member.type.has(type::q_static))
    913 				parse_success = false,
    914 				error(member.location, 3007, '\'' + member.name + "': struct members cannot be declared 'static'");
    915 			if (member.type.has(type::q_uniform))
    916 				parse_success = false,
    917 				error(member.location, 3047, '\'' + member.name + "': struct members cannot be declared 'uniform'");
    918 			if (member.type.has(type::q_groupshared))
    919 				parse_success = false,
    920 				error(member.location, 3010, '\'' + member.name + "': struct members cannot be declared 'groupshared'");
    921 
    922 			// Modify member specific type, so that following members in the declaration list are not affected by this
    923 			if (!parse_array_size(member.type))
    924 				return consume_until('}'), accept(';'), false;
    925 			else if (member.type.array_length < 0)
    926 				parse_success = false,
    927 				error(member.location, 3072, '\'' + member.name + "': array dimensions of struct members must be explicit");
    928 
    929 			// Structure members may have semantics to use them as input/output types
    930 			if (accept(':'))
    931 			{
    932 				if (!expect(tokenid::identifier))
    933 					return consume_until('}'), accept(';'), false;
    934 
    935 				member.semantic = std::move(_token.literal_as_string);
    936 				// Make semantic upper case to simplify comparison later on
    937 				std::transform(member.semantic.begin(), member.semantic.end(), member.semantic.begin(),
    938 					[](std::string::value_type c) {
    939 						return static_cast<std::string::value_type>(std::toupper(c));
    940 					});
    941 
    942 				if (member.semantic.compare(0, 3, "SV_") != 0)
    943 				{
    944 					// Always numerate semantics, so that e.g. TEXCOORD and TEXCOORD0 point to the same location
    945 					if (const char c = member.semantic.back(); c < '0' || c > '9')
    946 						member.semantic += '0';
    947 
    948 					if (member.type.is_integral() && !member.type.has(type::q_nointerpolation))
    949 					{
    950 						member.type.qualifiers |= type::q_nointerpolation; // Integer fields do not interpolate, so make this explicit (to avoid issues with GLSL)
    951 						warning(member.location, 4568, '\'' + member.name + "': integer fields have the 'nointerpolation' qualifier by default");
    952 					}
    953 				}
    954 				else
    955 				{
    956 					// Remove optional trailing zero from system value semantics, so that e.g. SV_POSITION and SV_POSITION0 mean the same thing
    957 					if (member.semantic.back() == '0' && (member.semantic[member.semantic.size() - 2] < '0' || member.semantic[member.semantic.size() - 2] > '9'))
    958 						member.semantic.pop_back();
    959 				}
    960 			}
    961 
    962 			// Save member name and type for book keeping
    963 			info.member_list.push_back(member);
    964 		} while (!peek(';'));
    965 
    966 		if (!expect(';'))
    967 			return consume_until('}'), accept(';'), false;
    968 	}
    969 
    970 	// Empty structures are valid, but not usually intended, so emit a warning
    971 	if (info.member_list.empty())
    972 		warning(location, 5001, "struct has no members");
    973 
    974 	// Define the structure now that information about all the member types was gathered
    975 	const auto id = _codegen->define_struct(location, info);
    976 
    977 	// Insert the symbol into the symbol table
    978 	const symbol symbol = { symbol_type::structure, id };
    979 
    980 	if (!insert_symbol(info.name, symbol, true))
    981 		return error(location, 3003, "redefinition of '" + info.name + '\''), false;
    982 
    983 	return expect('}') && parse_success;
    984 }
    985 
    986 bool reshadefx::parser::parse_function(type type, std::string name)
    987 {
    988 	const auto location = std::move(_token.location);
    989 
    990 	if (!expect('(')) // Functions always have a parameter list
    991 		return false;
    992 	if (type.qualifiers != 0)
    993 		return error(location, 3047, '\'' + name + "': function return type cannot have any qualifiers"), false;
    994 
    995 	function_info info;
    996 	info.name = name;
    997 	info.unique_name = 'F' + current_scope().name + name;
    998 	std::replace(info.unique_name.begin(), info.unique_name.end(), ':', '_');
    999 
   1000 	info.return_type = type;
   1001 	_current_function = &info;
   1002 
   1003 	bool parse_success = true;
   1004 	bool expect_parenthesis = true;
   1005 
   1006 	// Enter function scope (and leave it again when finished parsing this function)
   1007 	enter_scope();
   1008 	on_scope_exit _([this]() {
   1009 		leave_scope();
   1010 		_codegen->leave_function();
   1011 		_current_function = nullptr;
   1012 	});
   1013 
   1014 	while (!peek(')'))
   1015 	{
   1016 		if (!info.parameter_list.empty() && !expect(','))
   1017 		{
   1018 			parse_success = false;
   1019 			expect_parenthesis = false;
   1020 			consume_until(')');
   1021 			break;
   1022 		}
   1023 
   1024 		struct_member_info param;
   1025 
   1026 		if (!parse_type(param.type))
   1027 		{
   1028 			error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected parameter type");
   1029 			parse_success = false;
   1030 			expect_parenthesis = false;
   1031 			consume_until(')');
   1032 			break;
   1033 		}
   1034 
   1035 		if (!expect(tokenid::identifier))
   1036 		{
   1037 			parse_success = false;
   1038 			expect_parenthesis = false;
   1039 			consume_until(')');
   1040 			break;
   1041 		}
   1042 
   1043 		param.name = std::move(_token.literal_as_string);
   1044 		param.location = std::move(_token.location);
   1045 
   1046 		if (param.type.is_void())
   1047 			parse_success = false,
   1048 			error(param.location, 3038, '\'' + param.name + "': function parameters cannot be void");
   1049 
   1050 		if (param.type.has(type::q_extern))
   1051 			parse_success = false,
   1052 			error(param.location, 3006, '\'' + param.name + "': function parameters cannot be declared 'extern'");
   1053 		if (param.type.has(type::q_static))
   1054 			parse_success = false,
   1055 			error(param.location, 3007, '\'' + param.name + "': function parameters cannot be declared 'static'");
   1056 		if (param.type.has(type::q_uniform))
   1057 			parse_success = false,
   1058 			error(param.location, 3047, '\'' + param.name + "': function parameters cannot be declared 'uniform', consider placing in global scope instead");
   1059 		if (param.type.has(type::q_groupshared))
   1060 			parse_success = false,
   1061 			error(param.location, 3010, '\'' + param.name + "': function parameters cannot be declared 'groupshared'");
   1062 
   1063 		if (param.type.has(type::q_out) && param.type.has(type::q_const))
   1064 			parse_success = false,
   1065 			error(param.location, 3046, '\'' + param.name + "': output parameters cannot be declared 'const'");
   1066 		else if (!param.type.has(type::q_out))
   1067 			param.type.qualifiers |= type::q_in; // Function parameters are implicitly 'in' if not explicitly defined as 'out'
   1068 
   1069 		if (!parse_array_size(param.type))
   1070 		{
   1071 			parse_success = false;
   1072 			expect_parenthesis = false;
   1073 			consume_until(')');
   1074 			break;
   1075 		}
   1076 		else if (param.type.array_length < 0)
   1077 		{
   1078 			parse_success = false;
   1079 			error(param.location, 3072, '\'' + param.name + "': array dimensions of function parameters must be explicit");
   1080 		}
   1081 
   1082 		// Handle parameter type semantic
   1083 		if (accept(':'))
   1084 		{
   1085 			if (!expect(tokenid::identifier))
   1086 			{
   1087 				parse_success = false;
   1088 				expect_parenthesis = false;
   1089 				consume_until(')');
   1090 				break;
   1091 			}
   1092 
   1093 			param.semantic = std::move(_token.literal_as_string);
   1094 			// Make semantic upper case to simplify comparison later on
   1095 			std::transform(param.semantic.begin(), param.semantic.end(), param.semantic.begin(),
   1096 				[](std::string::value_type c) {
   1097 					return static_cast<std::string::value_type>(std::toupper(c));
   1098 				});
   1099 
   1100 			if (param.semantic.compare(0, 3, "SV_") != 0)
   1101 			{
   1102 				// Always numerate semantics, so that e.g. TEXCOORD and TEXCOORD0 point to the same location
   1103 				if (const char c = param.semantic.back(); c < '0' || c > '9')
   1104 					param.semantic += '0';
   1105 
   1106 				if (param.type.is_integral() && !param.type.has(type::q_nointerpolation))
   1107 				{
   1108 					param.type.qualifiers |= type::q_nointerpolation; // Integer parameters do not interpolate, so make this explicit (to avoid issues with GLSL)
   1109 					warning(param.location, 4568, '\'' + param.name + "': integer parameters have the 'nointerpolation' qualifier by default");
   1110 				}
   1111 			}
   1112 			else
   1113 			{
   1114 				// Remove optional trailing zero from system value semantics, so that e.g. SV_POSITION and SV_POSITION0 mean the same thing
   1115 				if (param.semantic.back() == '0' && (param.semantic[param.semantic.size() - 2] < '0' || param.semantic[param.semantic.size() - 2] > '9'))
   1116 					param.semantic.pop_back();
   1117 			}
   1118 		}
   1119 
   1120 		info.parameter_list.push_back(std::move(param));
   1121 	}
   1122 
   1123 	if (expect_parenthesis && !expect(')'))
   1124 		return false;
   1125 
   1126 	// Handle return type semantic
   1127 	if (accept(':'))
   1128 	{
   1129 		if (!expect(tokenid::identifier))
   1130 			return false;
   1131 		if (type.is_void())
   1132 			return error(_token.location, 3076, '\'' + name + "': void function cannot have a semantic"), false;
   1133 
   1134 		info.return_semantic = std::move(_token.literal_as_string);
   1135 		// Make semantic upper case to simplify comparison later on
   1136 		std::transform(info.return_semantic.begin(), info.return_semantic.end(), info.return_semantic.begin(),
   1137 			[](std::string::value_type c) {
   1138 				return static_cast<std::string::value_type>(std::toupper(c));
   1139 			});
   1140 	}
   1141 
   1142 	// Check if this is a function declaration without a body
   1143 	if (accept(';'))
   1144 		return error(location, 3510, '\'' + name + "': function is missing an implementation"), false;
   1145 
   1146 	// Define the function now that information about the declaration was gathered
   1147 	const auto id = _codegen->define_function(location, info);
   1148 
   1149 	// Insert the function and parameter symbols into the symbol table and update current function pointer to the permanent one
   1150 	symbol symbol = { symbol_type::function, id, { type::t_function } };
   1151 	symbol.function = _current_function = &_codegen->get_function(id);
   1152 
   1153 	if (!insert_symbol(name, symbol, true))
   1154 		return error(location, 3003, "redefinition of '" + name + '\''), false;
   1155 
   1156 	for (const struct_member_info &param : info.parameter_list)
   1157 		if (!insert_symbol(param.name, { symbol_type::variable, param.definition, param.type }))
   1158 			return error(param.location, 3003, "redefinition of '" + param.name + '\''), false;
   1159 
   1160 	// A function has to start with a new block
   1161 	_codegen->enter_block(_codegen->create_block());
   1162 
   1163 	if (!parse_statement_block(false))
   1164 		parse_success = false;
   1165 
   1166 	// Add implicit return statement to the end of functions
   1167 	if (_codegen->is_in_block())
   1168 		_codegen->leave_block_and_return();
   1169 
   1170 	return parse_success;
   1171 }
   1172 
   1173 bool reshadefx::parser::parse_variable(type type, std::string name, bool global)
   1174 {
   1175 	const auto location = std::move(_token.location);
   1176 
   1177 	if (type.is_void())
   1178 		return error(location, 3038, '\'' + name + "': variables cannot be void"), false;
   1179 	if (type.has(type::q_in) || type.has(type::q_out))
   1180 		return error(location, 3055, '\'' + name + "': variables cannot be declared 'in' or 'out'"), false;
   1181 
   1182 	// Local and global variables have different requirements
   1183 	if (global)
   1184 	{
   1185 		// Check that type qualifier combinations are valid
   1186 		if (type.has(type::q_static))
   1187 		{
   1188 			// Global variables that are 'static' cannot be of another storage class
   1189 			if (type.has(type::q_uniform))
   1190 				return error(location, 3007, '\'' + name + "': uniform global variables cannot be declared 'static'"), false;
   1191 			// The 'volatile' qualifier is only valid memory object declarations that are storage images or uniform blocks
   1192 			if (type.has(type::q_volatile))
   1193 				return error(location, 3008, '\'' + name + "': global variables cannot be declared 'volatile'"), false;
   1194 		}
   1195 		else if (!type.has(type::q_groupshared))
   1196 		{
   1197 			// Make all global variables 'uniform' by default, since they should be externally visible without the 'static' keyword
   1198 			if (!type.has(type::q_uniform) && !type.is_object())
   1199 				warning(location, 5000, '\'' + name + "': global variables are considered 'uniform' by default");
   1200 
   1201 			// Global variables that are not 'static' are always 'extern' and 'uniform'
   1202 			type.qualifiers |= type::q_extern | type::q_uniform;
   1203 
   1204 			// It is invalid to make 'uniform' variables constant, since they can be modified externally
   1205 			if (type.has(type::q_const))
   1206 				return error(location, 3035, '\'' + name + "': variables which are 'uniform' cannot be declared 'const'"), false;
   1207 		}
   1208 	}
   1209 	else
   1210 	{
   1211 		// Static does not really have meaning on local variables
   1212 		if (type.has(type::q_static))
   1213 			type.qualifiers &= ~type::q_static;
   1214 
   1215 		if (type.has(type::q_extern))
   1216 			return error(location, 3006, '\'' + name + "': local variables cannot be declared 'extern'"), false;
   1217 		if (type.has(type::q_uniform))
   1218 			return error(location, 3047, '\'' + name + "': local variables cannot be declared 'uniform'"), false;
   1219 		if (type.has(type::q_groupshared))
   1220 			return error(location, 3010, '\'' + name + "': local variables cannot be declared 'groupshared'"), false;
   1221 
   1222 		if (type.is_object())
   1223 			return error(location, 3038, '\'' + name + "': local variables cannot be texture, sampler or storage objects"), false;
   1224 	}
   1225 
   1226 	// The variable name may be followed by an optional array size expression
   1227 	if (!parse_array_size(type))
   1228 		return false;
   1229 
   1230 	bool parse_success = true;
   1231 	expression initializer;
   1232 	texture_info texture_info;
   1233 	sampler_info sampler_info;
   1234 	storage_info storage_info;
   1235 
   1236 	if (accept(':'))
   1237 	{
   1238 		if (!expect(tokenid::identifier))
   1239 			return false;
   1240 		else if (!global) // Only global variables can have a semantic
   1241 			return error(_token.location, 3043, '\'' + name + "': local variables cannot have semantics"), false;
   1242 
   1243 		std::string &semantic = texture_info.semantic;
   1244 		semantic = std::move(_token.literal_as_string);
   1245 
   1246 		// Make semantic upper case to simplify comparison later on
   1247 		std::transform(semantic.begin(), semantic.end(), semantic.begin(),
   1248 			[](std::string::value_type c) {
   1249 				return static_cast<std::string::value_type>(std::toupper(c));
   1250 			});
   1251 	}
   1252 	else
   1253 	{
   1254 		// Global variables can have optional annotations
   1255 		if (global && !parse_annotations(sampler_info.annotations))
   1256 			parse_success = false;
   1257 
   1258 		// Variables without a semantic may have an optional initializer
   1259 		if (accept('='))
   1260 		{
   1261 			if (!parse_expression_assignment(initializer))
   1262 				return false;
   1263 
   1264 			if (type.has(type::q_groupshared))
   1265 				return error(initializer.location, 3009, '\'' + name + "': variables declared 'groupshared' cannot have an initializer"), false;
   1266 			// TODO: This could be resolved by initializing these at the beginning of the entry point
   1267 			if (global && !initializer.is_constant)
   1268 				return error(initializer.location, 3011, '\'' + name + "': initial value must be a literal expression"), false;
   1269 
   1270 			// Check type compatibility
   1271 			if ((type.array_length >= 0 && initializer.type.array_length != type.array_length) || !type::rank(initializer.type, type))
   1272 				return error(initializer.location, 3017, '\'' + name + "': initial value (" + initializer.type.description() + ") does not match variable type (" + type.description() + ')'), false;
   1273 			if ((initializer.type.rows < type.rows || initializer.type.cols < type.cols) && !initializer.type.is_scalar())
   1274 				return error(initializer.location, 3017, '\'' + name + "': cannot implicitly convert these vector types (from " + initializer.type.description() + " to " + type.description() + ')'), false;
   1275 
   1276 			// Deduce array size from the initializer expression
   1277 			if (initializer.type.is_array())
   1278 				type.array_length = initializer.type.array_length;
   1279 
   1280 			// Perform implicit cast from initializer expression to variable type
   1281 			if (initializer.type.components() > type.components())
   1282 				warning(initializer.location, 3206, "implicit truncation of vector type");
   1283 
   1284 			initializer.add_cast_operation(type);
   1285 
   1286 			if (type.has(type::q_static))
   1287 				initializer.type.qualifiers |= type::q_static;
   1288 		}
   1289 		else if (type.is_numeric() || type.is_struct()) // Numeric variables without an initializer need special handling
   1290 		{
   1291 			if (type.has(type::q_const)) // Constants have to have an initial value
   1292 				return error(location, 3012, '\'' + name + "': missing initial value"), false;
   1293 			else if (!type.has(type::q_uniform)) // Zero initialize all global variables
   1294 				initializer.reset_to_rvalue_constant(location, {}, type);
   1295 		}
   1296 		else if (global && accept('{')) // Textures and samplers can have a property block attached to their declaration
   1297 		{
   1298 			// Non-numeric variables cannot be constants
   1299 			if (type.has(type::q_const))
   1300 				return error(location, 3035, '\'' + name + "': this variable type cannot be declared 'const'"), false;
   1301 
   1302 			while (!peek('}'))
   1303 			{
   1304 				if (!expect(tokenid::identifier))
   1305 					return consume_until('}'), false;
   1306 
   1307 				const auto property_name = std::move(_token.literal_as_string);
   1308 				const auto property_location = std::move(_token.location);
   1309 
   1310 				if (!expect('='))
   1311 					return consume_until('}'), false;
   1312 
   1313 				backup();
   1314 
   1315 				expression expression;
   1316 
   1317 				if (accept(tokenid::identifier)) // Handle special enumeration names for property values
   1318 				{
   1319 					// Transform identifier to uppercase to do case-insensitive comparison
   1320 					std::transform(_token.literal_as_string.begin(), _token.literal_as_string.end(), _token.literal_as_string.begin(),
   1321 						[](std::string::value_type c) {
   1322 							return static_cast<std::string::value_type>(std::toupper(c));
   1323 						});
   1324 
   1325 					static const std::unordered_map<std::string_view, uint32_t> s_enum_values = {
   1326 						{ "NONE", 0 }, { "POINT", 0 },
   1327 						{ "LINEAR", 1 },
   1328 						{ "WRAP", uint32_t(texture_address_mode::wrap) }, { "REPEAT", uint32_t(texture_address_mode::wrap) },
   1329 						{ "MIRROR", uint32_t(texture_address_mode::mirror) },
   1330 						{ "CLAMP", uint32_t(texture_address_mode::clamp) },
   1331 						{ "BORDER", uint32_t(texture_address_mode::border) },
   1332 						{ "R8", uint32_t(texture_format::r8) },
   1333 						{ "R16", uint32_t(texture_format::r16) },
   1334 						{ "R16F", uint32_t(texture_format::r16f) },
   1335 						{ "R32I", uint32_t(texture_format::r32i) },
   1336 						{ "R32U", uint32_t(texture_format::r32u) },
   1337 						{ "R32F", uint32_t(texture_format::r32f) },
   1338 						{ "RG8", uint32_t(texture_format::rg8) }, { "R8G8", uint32_t(texture_format::rg8) },
   1339 						{ "RG16", uint32_t(texture_format::rg16) }, { "R16G16", uint32_t(texture_format::rg16) },
   1340 						{ "RG16F", uint32_t(texture_format::rg16f) }, { "R16G16F", uint32_t(texture_format::rg16f) },
   1341 						{ "RG32F", uint32_t(texture_format::rg32f) }, { "R32G32F", uint32_t(texture_format::rg32f) },
   1342 						{ "RGBA8", uint32_t(texture_format::rgba8) }, { "R8G8B8A8", uint32_t(texture_format::rgba8) },
   1343 						{ "RGBA16", uint32_t(texture_format::rgba16) }, { "R16G16B16A16", uint32_t(texture_format::rgba16) },
   1344 						{ "RGBA16F", uint32_t(texture_format::rgba16f) }, { "R16G16B16A16F", uint32_t(texture_format::rgba16f) },
   1345 						{ "RGBA32F", uint32_t(texture_format::rgba32f) }, { "R32G32B32A32F", uint32_t(texture_format::rgba32f) },
   1346 						{ "RGB10A2", uint32_t(texture_format::rgb10a2) }, { "R10G10B10A2", uint32_t(texture_format::rgb10a2) },
   1347 					};
   1348 
   1349 					// Look up identifier in list of possible enumeration names
   1350 					if (const auto it = s_enum_values.find(_token.literal_as_string);
   1351 						it != s_enum_values.end())
   1352 						expression.reset_to_rvalue_constant(_token.location, it->second);
   1353 					else // No match found, so rewind to parser state before the identifier was consumed and try parsing it as a normal expression
   1354 						restore();
   1355 				}
   1356 
   1357 				// Parse right hand side as normal expression if no special enumeration name was matched already
   1358 				if (!expression.is_constant && !parse_expression_multary(expression))
   1359 					return consume_until('}'), false;
   1360 
   1361 				if (property_name == "Texture")
   1362 				{
   1363 					// Ignore invalid symbols that were added during error recovery
   1364 					if (expression.base == 0xFFFFFFFF)
   1365 						return consume_until('}'), false;
   1366 
   1367 					if (!expression.type.is_texture())
   1368 						return error(expression.location, 3020, "type mismatch, expected texture name"), consume_until('}'), false;
   1369 
   1370 					if (type.is_sampler() || type.is_storage())
   1371 					{
   1372 						reshadefx::texture_info &target_info = _codegen->get_texture(expression.base);
   1373 						if (type.is_storage())
   1374 							// Texture is used as storage
   1375 							target_info.storage_access = true;
   1376 
   1377 						texture_info = target_info;
   1378 						sampler_info.texture_name = target_info.unique_name;
   1379 						storage_info.texture_name = target_info.unique_name;
   1380 					}
   1381 				}
   1382 				else
   1383 				{
   1384 					if (!expression.is_constant || !expression.type.is_scalar())
   1385 						return error(expression.location, 3538, "value must be a literal scalar expression"), consume_until('}'), false;
   1386 
   1387 					// All states below expect the value to be of an integer type
   1388 					expression.add_cast_operation({ type::t_int, 1, 1 });
   1389 					const int value = expression.constant.as_int[0];
   1390 
   1391 					if (value < 0) // There is little use for negative values, so warn in those cases
   1392 						warning(expression.location, 3571, "negative value specified for property '" + property_name + '\'');
   1393 
   1394 					if (type.is_texture())
   1395 					{
   1396 						if (property_name == "Width")
   1397 							texture_info.width = value > 0 ? value : 1;
   1398 						else if (type.texture_dimension() >= 2 && property_name == "Height")
   1399 							texture_info.height = value > 0 ? value : 1;
   1400 						else if (type.texture_dimension() >= 3 && property_name == "Depth")
   1401 							texture_info.depth = value > 0 && value <= std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 1;
   1402 						else if (property_name == "MipLevels")
   1403 							// Also ensures negative values do not cause problems
   1404 							texture_info.levels = value > 0 && value <= std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 1;
   1405 						else if (property_name == "Format")
   1406 							texture_info.format = static_cast<texture_format>(value);
   1407 						else
   1408 							return error(property_location, 3004, "unrecognized property '" + property_name + '\''), consume_until('}'), false;
   1409 					}
   1410 					else if (type.is_sampler())
   1411 					{
   1412 						if (property_name == "SRGBTexture" || property_name == "SRGBReadEnable")
   1413 							sampler_info.srgb = value != 0;
   1414 						else if (property_name == "AddressU")
   1415 							sampler_info.address_u = static_cast<texture_address_mode>(value);
   1416 						else if (property_name == "AddressV")
   1417 							sampler_info.address_v = static_cast<texture_address_mode>(value);
   1418 						else if (property_name == "AddressW")
   1419 							sampler_info.address_w = static_cast<texture_address_mode>(value);
   1420 						else if (property_name == "MinFilter")
   1421 							sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x0F) | ((value << 4) & 0x30)); // Combine sampler filter components into a single filter enumeration value
   1422 						else if (property_name == "MagFilter")
   1423 							sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x33) | ((value << 2) & 0x0C));
   1424 						else if (property_name == "MipFilter")
   1425 							sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x3C) |  (value       & 0x03));
   1426 						else if (property_name == "MinLOD" || property_name == "MaxMipLevel")
   1427 							sampler_info.min_lod = static_cast<float>(value);
   1428 						else if (property_name == "MaxLOD")
   1429 							sampler_info.max_lod = static_cast<float>(value);
   1430 						else if (property_name == "MipLODBias" || property_name == "MipMapLodBias")
   1431 							sampler_info.lod_bias = static_cast<float>(value);
   1432 						else
   1433 							return error(property_location, 3004, "unrecognized property '" + property_name + '\''), consume_until('}'), false;
   1434 					}
   1435 					else if (type.is_storage())
   1436 					{
   1437 						if (property_name == "MipLOD" || property_name == "MipLevel")
   1438 							storage_info.level = value > 0 && value < std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 0;
   1439 						else
   1440 							return error(property_location, 3004, "unrecognized property '" + property_name + '\''), consume_until('}'), false;
   1441 					}
   1442 				}
   1443 
   1444 				if (!expect(';'))
   1445 					return consume_until('}'), false;
   1446 			}
   1447 
   1448 			if (!expect('}'))
   1449 				return false;
   1450 		}
   1451 	}
   1452 
   1453 	// At this point the array size should be known (either from the declaration or the initializer)
   1454 	if (type.array_length < 0)
   1455 		return error(location, 3074, '\'' + name + "': implicit array missing initial value"), false;
   1456 
   1457 	symbol symbol;
   1458 
   1459 	// Variables with a constant initializer and constant type are named constants
   1460 	// Skip this for very large arrays though, to avoid large amounts of duplicated values when that array constant is accessed with a dynamic index
   1461 	if (type.is_numeric() && type.has(type::q_const) && initializer.is_constant && type.array_length < 100)
   1462 	{
   1463 		// Named constants are special symbols
   1464 		symbol = { symbol_type::constant, 0, type, initializer.constant };
   1465 	}
   1466 	else if (type.is_texture())
   1467 	{
   1468 		assert(global);
   1469 
   1470 		texture_info.name = name;
   1471 		texture_info.type = static_cast<texture_type>(type.texture_dimension());
   1472 
   1473 		// Add namespace scope to avoid name clashes
   1474 		texture_info.unique_name = 'V' + current_scope().name + name;
   1475 		std::replace(texture_info.unique_name.begin(), texture_info.unique_name.end(), ':', '_');
   1476 
   1477 		texture_info.annotations = std::move(sampler_info.annotations);
   1478 
   1479 		symbol = { symbol_type::variable, 0, type };
   1480 		symbol.id = _codegen->define_texture(location, texture_info);
   1481 	}
   1482 	// Samplers are actually combined image samplers
   1483 	else if (type.is_sampler())
   1484 	{
   1485 		assert(global);
   1486 
   1487 		if (sampler_info.texture_name.empty())
   1488 			return error(location, 3012, '\'' + name + "': missing 'Texture' property"), false;
   1489 		if (type.texture_dimension() != static_cast<unsigned int>(texture_info.type))
   1490 			return error(location, 3521, '\'' + name + "': type mismatch between texture and sampler type"), false;
   1491 		if (sampler_info.srgb && texture_info.format != texture_format::rgba8)
   1492 			return error(location, 4582, '\'' + name + "': texture does not support sRGB sampling (only textures with RGBA8 format do)"), false;
   1493 
   1494 		if (texture_info.format == texture_format::r32i ?
   1495 				!type.is_integral() || !type.is_signed() :
   1496 			texture_info.format == texture_format::r32u ?
   1497 				!type.is_integral() || !type.is_unsigned() :
   1498 				!type.is_floating_point())
   1499 			return error(location, 4582, '\'' + name + "': type mismatch between texture format and sampler element type"), false;
   1500 
   1501 		sampler_info.name = name;
   1502 		sampler_info.type = type;
   1503 
   1504 		// Add namespace scope to avoid name clashes
   1505 		sampler_info.unique_name = 'V' + current_scope().name + name;
   1506 		std::replace(sampler_info.unique_name.begin(), sampler_info.unique_name.end(), ':', '_');
   1507 
   1508 		symbol = { symbol_type::variable, 0, type };
   1509 		symbol.id = _codegen->define_sampler(location, texture_info, sampler_info);
   1510 	}
   1511 	else if (type.is_storage())
   1512 	{
   1513 		assert(global);
   1514 
   1515 		if (storage_info.texture_name.empty())
   1516 			return error(location, 3012, '\'' + name + "': missing 'Texture' property"), false;
   1517 		if (type.texture_dimension() != static_cast<unsigned int>(texture_info.type))
   1518 			return error(location, 3521, '\'' + name + "': type mismatch between texture and storage type"), false;
   1519 
   1520 		if (texture_info.format == texture_format::r32i ?
   1521 				!type.is_integral() || !type.is_signed() :
   1522 			texture_info.format == texture_format::r32u ?
   1523 				!type.is_integral() || !type.is_unsigned() :
   1524 				!type.is_floating_point())
   1525 			return error(location, 4582, '\'' + name + "': type mismatch between texture format and storage element type"), false;
   1526 
   1527 		storage_info.name = name;
   1528 		storage_info.type = type;
   1529 
   1530 		// Add namespace scope to avoid name clashes
   1531 		storage_info.unique_name = 'V' + current_scope().name + name;
   1532 		std::replace(storage_info.unique_name.begin(), storage_info.unique_name.end(), ':', '_');
   1533 
   1534 		if (storage_info.level > texture_info.levels - 1)
   1535 			storage_info.level = texture_info.levels - 1;
   1536 
   1537 		symbol = { symbol_type::variable, 0, type };
   1538 		symbol.id = _codegen->define_storage(location, texture_info, storage_info);
   1539 	}
   1540 	// Uniform variables are put into a global uniform buffer structure
   1541 	else if (type.has(type::q_uniform))
   1542 	{
   1543 		assert(global);
   1544 
   1545 		uniform_info uniform_info;
   1546 		uniform_info.name = name;
   1547 		uniform_info.type = type;
   1548 
   1549 		uniform_info.annotations = std::move(sampler_info.annotations);
   1550 
   1551 		uniform_info.initializer_value = std::move(initializer.constant);
   1552 		uniform_info.has_initializer_value = initializer.is_constant;
   1553 
   1554 		symbol = { symbol_type::variable, 0, type };
   1555 		symbol.id = _codegen->define_uniform(location, uniform_info);
   1556 	}
   1557 	// All other variables are separate entities
   1558 	else
   1559 	{
   1560 		// Update global variable names to contain the namespace scope to avoid name clashes
   1561 		std::string unique_name = global ? 'V' + current_scope().name + name : name;
   1562 		std::replace(unique_name.begin(), unique_name.end(), ':', '_');
   1563 
   1564 		symbol = { symbol_type::variable, 0, type };
   1565 		symbol.id = _codegen->define_variable(location, type, std::move(unique_name), global,
   1566 			// Shared variables cannot have an initializer
   1567 			type.has(type::q_groupshared) ? 0 : _codegen->emit_load(initializer));
   1568 	}
   1569 
   1570 	// Insert the symbol into the symbol table
   1571 	if (!insert_symbol(name, symbol, global))
   1572 		return error(location, 3003, "redefinition of '" + name + '\''), false;
   1573 
   1574 	return parse_success;
   1575 }
   1576 
   1577 bool reshadefx::parser::parse_technique()
   1578 {
   1579 	if (!expect(tokenid::identifier))
   1580 		return false;
   1581 
   1582 	technique_info info;
   1583 	info.name = std::move(_token.literal_as_string);
   1584 
   1585 	bool parse_success = parse_annotations(info.annotations);
   1586 
   1587 	if (!expect('{'))
   1588 		return false;
   1589 
   1590 	while (!peek('}'))
   1591 	{
   1592 		if (pass_info pass; parse_technique_pass(pass))
   1593 			info.passes.push_back(std::move(pass));
   1594 		else {
   1595 			parse_success = false;
   1596 			if (!peek(tokenid::pass) && !peek('}')) // If there is another pass definition following, try to parse that despite the error
   1597 				return consume_until('}'), false;
   1598 		}
   1599 	}
   1600 
   1601 	_codegen->define_technique(std::move(info));
   1602 
   1603 	return expect('}') && parse_success;
   1604 }
   1605 bool reshadefx::parser::parse_technique_pass(pass_info &info)
   1606 {
   1607 	if (!expect(tokenid::pass))
   1608 		return false;
   1609 
   1610 	const auto pass_location = std::move(_token.location);
   1611 
   1612 	// Passes can have an optional name
   1613 	if (accept(tokenid::identifier))
   1614 		info.name = std::move(_token.literal_as_string);
   1615 
   1616 	bool parse_success = true;
   1617 	bool targets_support_srgb = true;
   1618 	function_info vs_info, ps_info, cs_info;
   1619 
   1620 	if (!expect('{'))
   1621 		return false;
   1622 
   1623 	while (!peek('}'))
   1624 	{
   1625 		// Parse pass states
   1626 		if (!expect(tokenid::identifier))
   1627 			return consume_until('}'), false;
   1628 
   1629 		auto location = std::move(_token.location);
   1630 		const auto state = std::move(_token.literal_as_string);
   1631 
   1632 		if (!expect('='))
   1633 			return consume_until('}'), false;
   1634 
   1635 		const bool is_shader_state = state == "VertexShader" || state == "PixelShader" || state == "ComputeShader";
   1636 		const bool is_texture_state = state.compare(0, 12, "RenderTarget") == 0 && (state.size() == 12 || (state[12] >= '0' && state[12] < '8'));
   1637 
   1638 		// Shader and render target assignment looks up values in the symbol table, so handle those separately from the other states
   1639 		if (is_shader_state || is_texture_state)
   1640 		{
   1641 			std::string identifier;
   1642 			scoped_symbol symbol;
   1643 			if (!accept_symbol(identifier, symbol))
   1644 				return consume_until('}'), false;
   1645 
   1646 			location = std::move(_token.location);
   1647 
   1648 			int num_threads[3] = { 1, 1, 1 };
   1649 			if (accept('<'))
   1650 			{
   1651 				expression x, y, z;
   1652 				if (!parse_expression_multary(x, 8) || !expect(',') || !parse_expression_multary(y, 8))
   1653 					return consume_until('}'), false;
   1654 
   1655 				// Parse optional third dimension (defaults to 1)
   1656 				z.reset_to_rvalue_constant({}, 1);
   1657 				if (accept(',') && !parse_expression_multary(z, 8))
   1658 					return consume_until('}'), false;
   1659 
   1660 				if (!x.is_constant)
   1661 					return error(x.location, 3011, "value must be a literal expression"), consume_until('}'), false;
   1662 				if (!y.is_constant)
   1663 					return error(y.location, 3011, "value must be a literal expression"), consume_until('}'), false;
   1664 				if (!z.is_constant)
   1665 					return error(z.location, 3011, "value must be a literal expression"), consume_until('}'), false;
   1666 				x.add_cast_operation({ type::t_int, 1, 1 });
   1667 				y.add_cast_operation({ type::t_int, 1, 1 });
   1668 				z.add_cast_operation({ type::t_int, 1, 1 });
   1669 				num_threads[0] = x.constant.as_int[0];
   1670 				num_threads[1] = y.constant.as_int[0];
   1671 				num_threads[2] = z.constant.as_int[0];
   1672 
   1673 				if (!expect('>'))
   1674 					return consume_until('}'), false;
   1675 			}
   1676 
   1677 			// Ignore invalid symbols that were added during error recovery
   1678 			if (symbol.id != 0xFFFFFFFF)
   1679 			{
   1680 				if (is_shader_state)
   1681 				{
   1682 					if (!symbol.id)
   1683 						parse_success = false,
   1684 						error(location, 3501, "undeclared identifier '" + identifier + "', expected function name");
   1685 					else if (!symbol.type.is_function())
   1686 						parse_success = false,
   1687 						error(location, 3020, "type mismatch, expected function name");
   1688 					else {
   1689 						// Look up the matching function info for this function definition
   1690 						function_info &function_info = _codegen->get_function(symbol.id);
   1691 
   1692 						// We potentially need to generate a special entry point function which translates between function parameters and input/output variables
   1693 						switch (state[0])
   1694 						{
   1695 						case 'V':
   1696 							vs_info = function_info;
   1697 							_codegen->define_entry_point(vs_info, shader_type::vs);
   1698 							info.vs_entry_point = vs_info.unique_name;
   1699 							break;
   1700 						case 'P':
   1701 							ps_info = function_info;
   1702 							_codegen->define_entry_point(ps_info, shader_type::ps);
   1703 							info.ps_entry_point = ps_info.unique_name;
   1704 							break;
   1705 						case 'C':
   1706 							cs_info = function_info;
   1707 							_codegen->define_entry_point(cs_info, shader_type::cs, num_threads);
   1708 							info.cs_entry_point = cs_info.unique_name;
   1709 							break;
   1710 						}
   1711 					}
   1712 				}
   1713 				else
   1714 				{
   1715 					assert(is_texture_state);
   1716 
   1717 					if (!symbol.id)
   1718 						parse_success = false,
   1719 						error(location, 3004, "undeclared identifier '" + identifier + "', expected texture name");
   1720 					else if (!symbol.type.is_texture())
   1721 						parse_success = false,
   1722 						error(location, 3020, "type mismatch, expected texture name");
   1723 					else if (symbol.type.texture_dimension() != 2)
   1724 						parse_success = false,
   1725 						error(location, 3020, "cannot use texture" + std::to_string(symbol.type.texture_dimension()) + "D as render target");
   1726 					else {
   1727 						reshadefx::texture_info &target_info = _codegen->get_texture(symbol.id);
   1728 						// Texture is used as a render target
   1729 						target_info.render_target = true;
   1730 
   1731 						// Verify that all render targets in this pass have the same dimensions
   1732 						if (info.viewport_width != 0 && info.viewport_height != 0 && (target_info.width != info.viewport_width || target_info.height != info.viewport_height))
   1733 							parse_success = false,
   1734 							error(location, 4545, "cannot use multiple render targets with different texture dimensions (is " + std::to_string(target_info.width) + 'x' + std::to_string(target_info.height) + ", but expected " + std::to_string(info.viewport_width) + 'x' + std::to_string(info.viewport_height) + ')');
   1735 
   1736 						info.viewport_width = target_info.width;
   1737 						info.viewport_height = target_info.height;
   1738 
   1739 						const auto target_index = state.size() > 12 ? (state[12] - '0') : 0;
   1740 						info.render_target_names[target_index] = target_info.unique_name;
   1741 
   1742 						// Only RGBA8 format supports sRGB writes across all APIs
   1743 						if (target_info.format != texture_format::rgba8)
   1744 							targets_support_srgb = false;
   1745 					}
   1746 				}
   1747 			}
   1748 			else
   1749 			{
   1750 				parse_success = false;
   1751 			}
   1752 		}
   1753 		else // Handle the rest of the pass states
   1754 		{
   1755 			backup();
   1756 
   1757 			expression expression;
   1758 
   1759 			if (accept(tokenid::identifier)) // Handle special enumeration names for pass states
   1760 			{
   1761 				// Transform identifier to uppercase to do case-insensitive comparison
   1762 				std::transform(_token.literal_as_string.begin(), _token.literal_as_string.end(), _token.literal_as_string.begin(),
   1763 					[](std::string::value_type c) {
   1764 						return static_cast<std::string::value_type>(std::toupper(c));
   1765 					});
   1766 
   1767 				static const std::unordered_map<std::string_view, uint32_t> s_enum_values = {
   1768 					{ "NONE", 0 }, { "ZERO", 0 }, { "ONE", 1 },
   1769 					{ "ADD", uint32_t(pass_blend_op::add) },
   1770 					{ "SUBTRACT", uint32_t(pass_blend_op::subtract) },
   1771 					{ "REVSUBTRACT", uint32_t(pass_blend_op::reverse_subtract) },
   1772 					{ "MIN", uint32_t(pass_blend_op::min) },
   1773 					{ "MAX", uint32_t(pass_blend_op::max) },
   1774 					{ "SRCCOLOR", uint32_t(pass_blend_factor::source_color) },
   1775 					{ "INVSRCCOLOR", uint32_t(pass_blend_factor::one_minus_source_color) },
   1776 					{ "DESTCOLOR", uint32_t(pass_blend_factor::dest_color) },
   1777 					{ "INVDESTCOLOR", uint32_t(pass_blend_factor::one_minus_dest_color) },
   1778 					{ "SRCALPHA", uint32_t(pass_blend_factor::source_alpha) },
   1779 					{ "INVSRCALPHA", uint32_t(pass_blend_factor::one_minus_source_alpha) },
   1780 					{ "DESTALPHA", uint32_t(pass_blend_factor::dest_alpha) },
   1781 					{ "INVDESTALPHA", uint32_t(pass_blend_factor::one_minus_dest_alpha) },
   1782 					{ "KEEP", uint32_t(pass_stencil_op::keep) },
   1783 					{ "REPLACE", uint32_t(pass_stencil_op::replace) },
   1784 					{ "INVERT", uint32_t(pass_stencil_op::invert) },
   1785 					{ "INCR", uint32_t(pass_stencil_op::increment) },
   1786 					{ "INCRSAT", uint32_t(pass_stencil_op::increment_saturate) },
   1787 					{ "DECR", uint32_t(pass_stencil_op::decrement) },
   1788 					{ "DECRSAT", uint32_t(pass_stencil_op::decrement_saturate) },
   1789 					{ "NEVER", uint32_t(pass_stencil_func::never) },
   1790 					{ "EQUAL", uint32_t(pass_stencil_func::equal) },
   1791 					{ "NEQUAL", uint32_t(pass_stencil_func::not_equal) }, { "NOTEQUAL", uint32_t(pass_stencil_func::not_equal)  },
   1792 					{ "LESS", uint32_t(pass_stencil_func::less) },
   1793 					{ "GREATER", uint32_t(pass_stencil_func::greater) },
   1794 					{ "LEQUAL", uint32_t(pass_stencil_func::less_equal) }, { "LESSEQUAL", uint32_t(pass_stencil_func::less_equal) },
   1795 					{ "GEQUAL", uint32_t(pass_stencil_func::greater_equal) }, { "GREATEREQUAL", uint32_t(pass_stencil_func::greater_equal) },
   1796 					{ "ALWAYS", uint32_t(pass_stencil_func::always) },
   1797 					{ "POINTS", uint32_t(primitive_topology::point_list) },
   1798 					{ "POINTLIST", uint32_t(primitive_topology::point_list) },
   1799 					{ "LINES", uint32_t(primitive_topology::line_list) },
   1800 					{ "LINELIST", uint32_t(primitive_topology::line_list) },
   1801 					{ "LINESTRIP", uint32_t(primitive_topology::line_strip) },
   1802 					{ "TRIANGLES", uint32_t(primitive_topology::triangle_list) },
   1803 					{ "TRIANGLELIST", uint32_t(primitive_topology::triangle_list) },
   1804 					{ "TRIANGLESTRIP", uint32_t(primitive_topology::triangle_strip) },
   1805 				};
   1806 
   1807 				// Look up identifier in list of possible enumeration names
   1808 				if (const auto it = s_enum_values.find(_token.literal_as_string);
   1809 					it != s_enum_values.end())
   1810 					expression.reset_to_rvalue_constant(_token.location, it->second);
   1811 				else // No match found, so rewind to parser state before the identifier was consumed and try parsing it as a normal expression
   1812 					restore();
   1813 			}
   1814 
   1815 			// Parse right hand side as normal expression if no special enumeration name was matched already
   1816 			if (!expression.is_constant && !parse_expression_multary(expression))
   1817 				return consume_until('}'), false;
   1818 			else if (!expression.is_constant || !expression.type.is_scalar())
   1819 				parse_success = false,
   1820 				error(expression.location, 3011, "pass state value must be a literal scalar expression");
   1821 
   1822 			// All states below expect the value to be of an unsigned integer type
   1823 			expression.add_cast_operation({ type::t_uint, 1, 1 });
   1824 			const unsigned int value = expression.constant.as_uint[0];
   1825 
   1826 #define SET_STATE_VALUE_INDEXED(name, info_name, value) \
   1827 	else if (constexpr size_t name##_len = sizeof(#name) - 1; state.compare(0, name##_len, #name) == 0 && (state.size() == name##_len || (state[name##_len] >= '0' && state[name##_len] < ('0' + static_cast<char>(std::size(info.info_name)))))) \
   1828 	{ \
   1829 		if (state.size() != name##_len) \
   1830 			info.info_name[state[name##_len] - '0'] = (value); \
   1831 		else \
   1832 			for (int i = 0; i < static_cast<int>(std::size(info.info_name)); ++i) \
   1833 				info.info_name[i] = (value); \
   1834 	}
   1835 
   1836 			if (state == "SRGBWriteEnable")
   1837 				info.srgb_write_enable = (value != 0);
   1838 			SET_STATE_VALUE_INDEXED(BlendEnable, blend_enable, value != 0)
   1839 			else if (state == "StencilEnable")
   1840 				info.stencil_enable = (value != 0);
   1841 			else if (state == "ClearRenderTargets")
   1842 				info.clear_render_targets = (value != 0);
   1843 			SET_STATE_VALUE_INDEXED(ColorWriteMask, color_write_mask, value & 0xFF)
   1844 			SET_STATE_VALUE_INDEXED(RenderTargetWriteMask, color_write_mask, value & 0xFF)
   1845 			else if (state == "StencilReadMask" || state == "StencilMask")
   1846 				info.stencil_read_mask = value & 0xFF;
   1847 			else if (state == "StencilWriteMask")
   1848 				info.stencil_write_mask = value & 0xFF;
   1849 			SET_STATE_VALUE_INDEXED(BlendOp, blend_op, static_cast<pass_blend_op>(value))
   1850 			SET_STATE_VALUE_INDEXED(BlendOpAlpha, blend_op_alpha, static_cast<pass_blend_op>(value))
   1851 			SET_STATE_VALUE_INDEXED(SrcBlend, src_blend, static_cast<pass_blend_factor>(value))
   1852 			SET_STATE_VALUE_INDEXED(SrcBlendAlpha, src_blend_alpha, static_cast<pass_blend_factor>(value))
   1853 			SET_STATE_VALUE_INDEXED(DestBlend, dest_blend, static_cast<pass_blend_factor>(value))
   1854 			SET_STATE_VALUE_INDEXED(DestBlendAlpha, dest_blend_alpha, static_cast<pass_blend_factor>(value))
   1855 			else if (state == "StencilFunc")
   1856 				info.stencil_comparison_func = static_cast<pass_stencil_func>(value);
   1857 			else if (state == "StencilRef")
   1858 				info.stencil_reference_value = value;
   1859 			else if (state == "StencilPass" || state == "StencilPassOp")
   1860 				info.stencil_op_pass = static_cast<pass_stencil_op>(value);
   1861 			else if (state == "StencilFail" || state == "StencilFailOp")
   1862 				info.stencil_op_fail = static_cast<pass_stencil_op>(value);
   1863 			else if (state == "StencilZFail" || state == "StencilDepthFail" || state == "StencilDepthFailOp")
   1864 				info.stencil_op_depth_fail = static_cast<pass_stencil_op>(value);
   1865 			else if (state == "VertexCount")
   1866 				info.num_vertices = value;
   1867 			else if (state == "PrimitiveType" || state == "PrimitiveTopology")
   1868 				info.topology = static_cast<primitive_topology>(value);
   1869 			else if (state == "DispatchSizeX")
   1870 				info.viewport_width = value;
   1871 			else if (state == "DispatchSizeY")
   1872 				info.viewport_height = value;
   1873 			else if (state == "DispatchSizeZ")
   1874 				info.viewport_dispatch_z = value;
   1875 			else if (state == "GenerateMipmaps" || state == "GenerateMipMaps")
   1876 				info.generate_mipmaps = (value != 0);
   1877 			else
   1878 				parse_success = false,
   1879 				error(location, 3004, "unrecognized pass state '" + state + '\'');
   1880 
   1881 #undef SET_STATE_VALUE_INDEXED
   1882 		}
   1883 
   1884 		if (!expect(';'))
   1885 			return consume_until('}'), false;
   1886 	}
   1887 
   1888 	if (parse_success)
   1889 	{
   1890 		if (!info.cs_entry_point.empty())
   1891 		{
   1892 			if (info.viewport_width == 0 || info.viewport_height == 0)
   1893 			{
   1894 				parse_success = false;
   1895 				error(pass_location, 3012, "pass is missing 'DispatchSizeX' or 'DispatchSizeY' property");
   1896 			}
   1897 
   1898 			if (!info.vs_entry_point.empty())
   1899 				warning(pass_location, 3089, "pass is specifying both 'VertexShader' and 'ComputeShader' which cannot be used together");
   1900 			if (!info.ps_entry_point.empty())
   1901 				warning(pass_location, 3089,  "pass is specifying both 'PixelShader' and 'ComputeShader' which cannot be used together");
   1902 
   1903 			for (codegen::id id : cs_info.referenced_samplers)
   1904 				info.samplers.push_back(_codegen->get_sampler(id));
   1905 			for (codegen::id id : cs_info.referenced_storages)
   1906 				info.storages.push_back(_codegen->get_storage(id));
   1907 		}
   1908 		else if (info.vs_entry_point.empty() || info.ps_entry_point.empty())
   1909 		{
   1910 			parse_success = false;
   1911 
   1912 			if (info.vs_entry_point.empty())
   1913 				error(pass_location, 3012, "pass is missing 'VertexShader' property");
   1914 			if (info.ps_entry_point.empty())
   1915 				error(pass_location, 3012,  "pass is missing 'PixelShader' property");
   1916 		}
   1917 		else
   1918 		{
   1919 			// Verify that shader signatures between VS and PS match (both semantics and interpolation qualifiers)
   1920 			std::unordered_map<std::string_view, type> vs_semantic_mapping;
   1921 			if (vs_info.return_semantic.empty())
   1922 			{
   1923 				if (!vs_info.return_type.is_void() && !vs_info.return_type.is_struct())
   1924 				{
   1925 					parse_success = false;
   1926 					error(pass_location, 3503, '\'' + vs_info.name + "': function return value is missing semantics");
   1927 				}
   1928 			}
   1929 			else
   1930 			{
   1931 				vs_semantic_mapping[vs_info.return_semantic] = vs_info.return_type;
   1932 			}
   1933 
   1934 			for (const struct_member_info &param : vs_info.parameter_list)
   1935 			{
   1936 				if (param.semantic.empty())
   1937 				{
   1938 					if (!param.type.is_struct())
   1939 					{
   1940 						parse_success = false;
   1941 						if (param.type.has(type::q_in))
   1942 							error(pass_location, 3502, '\'' + vs_info.name + "': input parameter '" + param.name + "' is missing semantics");
   1943 						else
   1944 							error(pass_location, 3503, '\'' + vs_info.name + "': output parameter '" + param.name + "' is missing semantics");
   1945 					}
   1946 				}
   1947 				else if (param.type.has(type::q_out))
   1948 				{
   1949 					vs_semantic_mapping[param.semantic] = param.type;
   1950 				}
   1951 			}
   1952 
   1953 			if (ps_info.return_semantic.empty())
   1954 			{
   1955 				if (!ps_info.return_type.is_void() && !ps_info.return_type.is_struct())
   1956 				{
   1957 					parse_success = false;
   1958 					error(pass_location, 3503, '\'' + ps_info.name + "': function return value is missing semantics");
   1959 				}
   1960 			}
   1961 
   1962 			for (const struct_member_info &param : ps_info.parameter_list)
   1963 			{
   1964 				if (param.semantic.empty())
   1965 				{
   1966 					if (!param.type.is_struct())
   1967 					{
   1968 						parse_success = false;
   1969 						if (param.type.has(type::q_in))
   1970 							error(pass_location, 3502, '\'' + ps_info.name + "': input parameter '" + param.name + "' is missing semantics");
   1971 						else
   1972 							error(pass_location, 3503, '\'' + ps_info.name + "': output parameter '" + param.name + "' is missing semantics");
   1973 					}
   1974 				}
   1975 				else if (param.type.has(type::q_in))
   1976 				{
   1977 					if (const auto it = vs_semantic_mapping.find(param.semantic);
   1978 						it == vs_semantic_mapping.end() || it->second != param.type)
   1979 						warning(pass_location, 4576, '\'' + ps_info.name + "': input parameter '" + param.name + "' semantic does not match vertex shader one");
   1980 					else if (((it->second.qualifiers ^ param.type.qualifiers) & (type::q_linear | type::q_noperspective | type::q_centroid | type::q_nointerpolation)) != 0)
   1981 						parse_success = false,
   1982 						error(  pass_location, 4568, '\'' + ps_info.name + "': input parameter '" + param.name + "' interpolation qualifiers do not match vertex shader ones");
   1983 				}
   1984 			}
   1985 
   1986 			for (codegen::id id : vs_info.referenced_samplers)
   1987 				info.samplers.push_back(_codegen->get_sampler(id));
   1988 			for (codegen::id id : ps_info.referenced_samplers)
   1989 				info.samplers.push_back(_codegen->get_sampler(id));
   1990 			if (!vs_info.referenced_storages.empty() || !ps_info.referenced_storages.empty())
   1991 			{
   1992 				parse_success = false;
   1993 				error(pass_location, 3667, "storage writes are only valid in compute shaders");
   1994 			}
   1995 		}
   1996 
   1997 		// Verify render target format supports sRGB writes if enabled
   1998 		if (info.srgb_write_enable && !targets_support_srgb)
   1999 			parse_success = false,
   2000 			error(pass_location, 4582, "one or more render target(s) do not support sRGB writes (only textures with RGBA8 format do)");
   2001 	}
   2002 
   2003 	return expect('}') && parse_success;
   2004 }