Path: blob/master/dep/reshadefx/src/effect_parser_exp.cpp
4246 views
/*1* Copyright (C) 2014 Patrick Mours2* SPDX-License-Identifier: BSD-3-Clause3*/45#include "effect_lexer.hpp"6#include "effect_parser.hpp"7#include "effect_codegen.hpp"8#include <cassert>9#include <iterator> // std::back_inserter10#include <algorithm> // std::lower_bound, std::set_union1112#define RESHADEFX_SHORT_CIRCUIT 01314reshadefx::parser::parser()15{16}17reshadefx::parser::~parser()18{19}2021void reshadefx::parser::error(const location &location, unsigned int code, const std::string &message)22{23_errors += location.source;24_errors += '(' + std::to_string(location.line) + ", " + std::to_string(location.column) + ')';25_errors += ": error";26if (code != 0)27_errors += " X" + std::to_string(code);28_errors += ": ";29_errors += message;30_errors += '\n';31}32void reshadefx::parser::warning(const location &location, unsigned int code, const std::string &message)33{34_errors += location.source;35_errors += '(' + std::to_string(location.line) + ", " + std::to_string(location.column) + ')';36_errors += ": warning";37if (code != 0)38_errors += " X" + std::to_string(code);39_errors += ": ";40_errors += message;41_errors += '\n';42}4344void reshadefx::parser::backup()45{46_token_backup = _token_next;47}48void reshadefx::parser::restore()49{50_lexer->reset_to_offset(_token_backup.offset + _token_backup.length);51_token_next = _token_backup; // Copy instead of move here, since restore may be called twice (from 'accept_type_class' and then again from 'parse_expression_unary')52}5354void reshadefx::parser::consume()55{56_token = std::move(_token_next);57_token_next = _lexer->lex();58}59void reshadefx::parser::consume_until(tokenid tokid)60{61while (!accept(tokid) && !peek(tokenid::end_of_file))62{63consume();64}65}6667bool reshadefx::parser::accept(tokenid tokid)68{69if (peek(tokid))70{71consume();72return true;73}7475return false;76}77bool reshadefx::parser::expect(tokenid tokid)78{79if (!accept(tokid))80{81error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected '" + token::id_to_name(tokid) + '\'');82return false;83}8485return true;86}8788bool reshadefx::parser::accept_symbol(std::string &identifier, scoped_symbol &symbol)89{90// Starting an identifier with '::' restricts the symbol search to the global namespace level91const bool exclusive = accept(tokenid::colon_colon);9293if (exclusive ? !expect(tokenid::identifier) : !accept(tokenid::identifier))94{95// No token should come through here, since all possible prefix expressions should have been handled above, so this is an error in the syntax96if (!exclusive)97error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + '\'');98return false;99}100101identifier = std::move(_token.literal_as_string);102103// Can concatenate multiple '::' to force symbol search for a specific namespace level104while (accept(tokenid::colon_colon))105{106if (!expect(tokenid::identifier))107return false;108identifier += "::" + std::move(_token.literal_as_string);109}110111// Figure out which scope to start searching in112scope scope = { "::", 0, 0 };113if (!exclusive)114scope = current_scope();115116// Lookup name in the symbol table117symbol = find_symbol(identifier, scope, exclusive);118119return true;120}121bool reshadefx::parser::accept_type_class(type &type)122{123type.rows = type.cols = 0;124125if (peek(tokenid::identifier) || peek(tokenid::colon_colon))126{127type.base = type::t_struct;128129backup(); // Need to restore if this identifier does not turn out to be a structure130131std::string identifier;132scoped_symbol symbol;133if (accept_symbol(identifier, symbol))134{135if (symbol.id && symbol.op == symbol_type::structure)136{137type.struct_definition = symbol.id;138return true;139}140}141142restore();143144return false;145}146147if (accept(tokenid::vector))148{149type.base = type::t_float; // Default to float4 unless a type is specified (see below)150type.rows = 4, type.cols = 1;151152if (accept('<'))153{154if (!accept_type_class(type)) // This overwrites the base type again155{156error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected vector element type");157return false;158}159else if (!type.is_scalar())160{161error(_token.location, 3122, "vector element type must be a scalar type");162return false;163}164165if (!expect(',') || !expect(tokenid::int_literal))166{167return false;168}169else if (_token.literal_as_int < 1 || _token.literal_as_int > 4)170{171error(_token.location, 3052, "vector dimension must be between 1 and 4");172return false;173}174175type.rows = static_cast<unsigned int>(_token.literal_as_int);176177if (!expect('>'))178return false;179}180181return true;182}183if (accept(tokenid::matrix))184{185type.base = type::t_float; // Default to float4x4 unless a type is specified (see below)186type.rows = 4, type.cols = 4;187188if (accept('<'))189{190if (!accept_type_class(type)) // This overwrites the base type again191{192error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected matrix element type");193return false;194}195else if (!type.is_scalar())196{197error(_token.location, 3123, "matrix element type must be a scalar type");198return false;199}200201if (!expect(',') || !expect(tokenid::int_literal))202{203return false;204}205else if (_token.literal_as_int < 1 || _token.literal_as_int > 4)206{207error(_token.location, 3053, "matrix dimensions must be between 1 and 4");208return false;209}210211type.rows = static_cast<unsigned int>(_token.literal_as_int);212213if (!expect(',') || !expect(tokenid::int_literal))214{215return false;216}217else if (_token.literal_as_int < 1 || _token.literal_as_int > 4)218{219error(_token.location, 3053, "matrix dimensions must be between 1 and 4");220return false;221}222223type.cols = static_cast<unsigned int>(_token.literal_as_int);224225if (!expect('>'))226return false;227}228229return true;230}231232if (accept(tokenid::sampler1d) || accept(tokenid::sampler2d) || accept(tokenid::sampler3d))233{234const unsigned int texture_dimension = static_cast<unsigned int>(_token.id) - static_cast<unsigned int>(tokenid::sampler1d);235236if (accept('<'))237{238if (!accept_type_class(type))239{240error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected sampler element type");241return false;242}243if (type.is_object())244{245error(_token.location, 3124, "object element type cannot be an object type");246return false;247}248if (!type.is_numeric() || type.is_matrix())249{250error(_token.location, 3521, "sampler element type must fit in four 32-bit quantities");251return false;252}253254if (type.is_integral() && type.is_signed())255type.base = static_cast<type::datatype>(type::t_sampler1d_int + texture_dimension);256else if (type.is_integral() && type.is_unsigned())257type.base = static_cast<type::datatype>(type::t_sampler1d_uint + texture_dimension);258else259type.base = static_cast<type::datatype>(type::t_sampler1d_float + texture_dimension);260261if (!expect('>'))262return false;263}264else265{266type.base = static_cast<type::datatype>(type::t_sampler1d_float + texture_dimension);267type.rows = 4;268type.cols = 1;269}270271return true;272}273if (accept(tokenid::storage1d) || accept(tokenid::storage2d) || accept(tokenid::storage3d))274{275const unsigned int texture_dimension = static_cast<unsigned int>(_token.id) - static_cast<unsigned int>(tokenid::storage1d);276277if (accept('<'))278{279if (!accept_type_class(type))280{281error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected storage element type");282return false;283}284if (type.is_object())285{286error(_token.location, 3124, "object element type cannot be an object type");287return false;288}289if (!type.is_numeric() || type.is_matrix())290{291error(_token.location, 3521, "storage element type must fit in four 32-bit quantities");292return false;293}294295if (type.is_integral() && type.is_signed())296type.base = static_cast<type::datatype>(type::t_storage1d_int + texture_dimension);297else if (type.is_integral() && type.is_unsigned())298type.base = static_cast<type::datatype>(type::t_storage1d_uint + texture_dimension);299else300type.base = static_cast<type::datatype>(type::t_storage1d_float + texture_dimension);301302if (!expect('>'))303return false;304}305else306{307type.base = static_cast<type::datatype>(type::t_storage1d_float + texture_dimension);308type.rows = 4;309type.cols = 1;310}311312return true;313}314315switch (_token_next.id)316{317case tokenid::void_:318type.base = type::t_void;319break;320case tokenid::bool_:321case tokenid::bool2:322case tokenid::bool3:323case tokenid::bool4:324type.base = type::t_bool;325type.rows = 1 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::bool_));326type.cols = 1;327break;328case tokenid::bool2x2:329case tokenid::bool2x3:330case tokenid::bool2x4:331case tokenid::bool3x2:332case tokenid::bool3x3:333case tokenid::bool3x4:334case tokenid::bool4x2:335case tokenid::bool4x3:336case tokenid::bool4x4:337type.base = type::t_bool;338type.rows = 2 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::bool2x2)) / 3;339type.cols = 2 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::bool2x2)) % 3;340break;341case tokenid::int_:342case tokenid::int2:343case tokenid::int3:344case tokenid::int4:345type.base = type::t_int;346type.rows = 1 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::int_));347type.cols = 1;348break;349case tokenid::int2x2:350case tokenid::int2x3:351case tokenid::int2x4:352case tokenid::int3x2:353case tokenid::int3x3:354case tokenid::int3x4:355case tokenid::int4x2:356case tokenid::int4x3:357case tokenid::int4x4:358type.base = type::t_int;359type.rows = 2 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::int2x2)) / 3;360type.cols = 2 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::int2x2)) % 3;361break;362case tokenid::min16int:363case tokenid::min16int2:364case tokenid::min16int3:365case tokenid::min16int4:366type.base = type::t_min16int;367type.rows = 1 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::min16int));368type.cols = 1;369break;370case tokenid::uint_:371case tokenid::uint2:372case tokenid::uint3:373case tokenid::uint4:374type.base = type::t_uint;375type.rows = 1 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::uint_));376type.cols = 1;377break;378case tokenid::uint2x2:379case tokenid::uint2x3:380case tokenid::uint2x4:381case tokenid::uint3x2:382case tokenid::uint3x3:383case tokenid::uint3x4:384case tokenid::uint4x2:385case tokenid::uint4x3:386case tokenid::uint4x4:387type.base = type::t_uint;388type.rows = 2 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::uint2x2)) / 3;389type.cols = 2 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::uint2x2)) % 3;390break;391case tokenid::min16uint:392case tokenid::min16uint2:393case tokenid::min16uint3:394case tokenid::min16uint4:395type.base = type::t_min16uint;396type.rows = 1 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::min16uint));397type.cols = 1;398break;399case tokenid::float_:400case tokenid::float2:401case tokenid::float3:402case tokenid::float4:403type.base = type::t_float;404type.rows = 1 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::float_));405type.cols = 1;406break;407case tokenid::float2x2:408case tokenid::float2x3:409case tokenid::float2x4:410case tokenid::float3x2:411case tokenid::float3x3:412case tokenid::float3x4:413case tokenid::float4x2:414case tokenid::float4x3:415case tokenid::float4x4:416type.base = type::t_float;417type.rows = 2 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::float2x2)) / 3;418type.cols = 2 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::float2x2)) % 3;419break;420case tokenid::min16float:421case tokenid::min16float2:422case tokenid::min16float3:423case tokenid::min16float4:424type.base = type::t_min16float;425type.rows = 1 + (static_cast<unsigned int>(_token_next.id) - static_cast<unsigned int>(tokenid::min16float));426type.cols = 1;427break;428case tokenid::string_:429type.base = type::t_string;430break;431case tokenid::texture1d:432type.base = type::t_texture1d;433break;434case tokenid::texture2d:435type.base = type::t_texture2d;436break;437case tokenid::texture3d:438type.base = type::t_texture3d;439break;440default:441return false;442}443444consume();445446return true;447}448bool reshadefx::parser::accept_type_qualifiers(type &type)449{450unsigned int qualifiers = 0;451452// Storage453if (accept(tokenid::extern_))454qualifiers |= type::q_extern;455if (accept(tokenid::static_))456qualifiers |= type::q_static;457if (accept(tokenid::uniform_))458qualifiers |= type::q_uniform;459if (accept(tokenid::volatile_))460qualifiers |= type::q_volatile;461if (accept(tokenid::precise))462qualifiers |= type::q_precise;463if (accept(tokenid::groupshared))464qualifiers |= type::q_groupshared;465466if (accept(tokenid::in))467qualifiers |= type::q_in;468if (accept(tokenid::out))469qualifiers |= type::q_out;470if (accept(tokenid::inout))471qualifiers |= type::q_inout;472473// Modifiers474if (accept(tokenid::const_))475qualifiers |= type::q_const;476477// Interpolation478if (accept(tokenid::linear))479qualifiers |= type::q_linear;480if (accept(tokenid::noperspective))481qualifiers |= type::q_noperspective;482if (accept(tokenid::centroid))483qualifiers |= type::q_centroid;484if (accept(tokenid::nointerpolation))485qualifiers |= type::q_nointerpolation;486487if (qualifiers == 0)488return false;489if ((type.qualifiers & qualifiers) == qualifiers)490warning(_token.location, 3048, "duplicate usages specified");491492type.qualifiers |= qualifiers;493494// Continue parsing potential additional qualifiers until no more are found495accept_type_qualifiers(type);496497return true;498}499500bool reshadefx::parser::accept_unary_op()501{502switch (_token_next.id)503{504case tokenid::exclaim: // !x (logical not)505case tokenid::plus: // +x506case tokenid::minus: // -x (negate)507case tokenid::tilde: // ~x (bitwise not)508case tokenid::plus_plus: // ++x509case tokenid::minus_minus: // --x510break;511default:512return false;513}514515consume();516517return true;518}519bool reshadefx::parser::accept_postfix_op()520{521switch (_token_next.id)522{523case tokenid::plus_plus: // ++x524case tokenid::minus_minus: // --x525break;526default:527return false;528}529530consume();531532return true;533}534bool reshadefx::parser::peek_multary_op(unsigned int &precedence) const535{536// Precedence values taken from https://cppreference.com/w/cpp/language/operator_precedence537switch (_token_next.id)538{539case tokenid::question: precedence = 1; break; // x ? a : b540case tokenid::pipe_pipe: precedence = 2; break; // a || b (logical or)541case tokenid::ampersand_ampersand: precedence = 3; break; // a && b (logical and)542case tokenid::pipe: precedence = 4; break; // a | b (bitwise or)543case tokenid::caret: precedence = 5; break; // a ^ b (bitwise xor)544case tokenid::ampersand: precedence = 6; break; // a & b (bitwise and)545case tokenid::equal_equal: precedence = 7; break; // a == b (equal)546case tokenid::exclaim_equal: precedence = 7; break; // a != b (not equal)547case tokenid::less: precedence = 8; break; // a < b548case tokenid::greater: precedence = 8; break; // a > b549case tokenid::less_equal: precedence = 8; break; // a <= b550case tokenid::greater_equal: precedence = 8; break; // a >= b551case tokenid::less_less: precedence = 9; break; // a << b (left shift)552case tokenid::greater_greater: precedence = 9; break; // a >> b (right shift)553case tokenid::plus: precedence = 10; break; // a + b (add)554case tokenid::minus: precedence = 10; break; // a - b (subtract)555case tokenid::star: precedence = 11; break; // a * b (multiply)556case tokenid::slash: precedence = 11; break; // a / b (divide)557case tokenid::percent: precedence = 11; break; // a % b (modulo)558default:559return false;560}561562// Do not consume token yet since the expression may be skipped due to precedence563return true;564}565bool reshadefx::parser::accept_assignment_op()566{567switch (_token_next.id)568{569case tokenid::equal: // a = b570case tokenid::percent_equal: // a %= b571case tokenid::ampersand_equal: // a &= b572case tokenid::star_equal: // a *= b573case tokenid::plus_equal: // a += b574case tokenid::minus_equal: // a -= b575case tokenid::slash_equal: // a /= b576case tokenid::less_less_equal: // a <<= b577case tokenid::greater_greater_equal: // a >>= b578case tokenid::caret_equal: // a ^= b579case tokenid::pipe_equal: // a |= b580break;581default:582return false;583}584585consume();586587return true;588}589590bool reshadefx::parser::parse_expression(expression &exp)591{592// Parse first expression593if (!parse_expression_assignment(exp))594return false;595596// Continue parsing if an expression sequence is next (in the form "a, b, c, ...")597while (accept(','))598{599// Overwrite 'exp' since conveniently the last expression in the sequence is the result600if (!parse_expression_assignment(exp))601return false;602}603604return true;605}606607bool reshadefx::parser::parse_expression_unary(expression &exp)608{609location location = _token_next.location;610611// Check if a prefix operator exists612if (accept_unary_op())613{614// Remember the operator token before parsing the expression that follows it615const tokenid op = _token.id;616617// Parse the actual expression618if (!parse_expression_unary(exp))619return false;620621// Unary operators are only valid on basic types622if (!exp.type.is_scalar() && !exp.type.is_vector() && !exp.type.is_matrix())623{624error(exp.location, 3022, "scalar, vector, or matrix expected");625return false;626}627628// Special handling for the "++" and "--" operators629if (op == tokenid::plus_plus || op == tokenid::minus_minus)630{631if (exp.type.has(type::q_const) || !exp.is_lvalue)632{633error(location, 3025, "l-value specifies const object");634return false;635}636637// Create a constant one in the type of the expression638const codegen::id constant_one = _codegen->emit_constant(exp.type, 1);639640const codegen::id value = _codegen->emit_load(exp);641const codegen::id result = _codegen->emit_binary_op(location, op, exp.type, value, constant_one);642643// The "++" and "--" operands modify the source variable, so store result back into it644_codegen->emit_store(exp, result);645}646else if (op != tokenid::plus) // Ignore "+" operator since it does not actually do anything647{648// The "~" bitwise operator is only valid on integral types649if (op == tokenid::tilde && !exp.type.is_integral())650{651error(exp.location, 3082, "int or unsigned int type required");652return false;653}654655// The logical not operator expects a boolean type as input, so perform cast if necessary656if (op == tokenid::exclaim && !exp.type.is_boolean())657exp.add_cast_operation({ type::t_bool, exp.type.rows, exp.type.cols }); // The result will be boolean as well658659// Constant expressions can be evaluated at compile time660if (!exp.evaluate_constant_expression(op))661{662const codegen::id value = _codegen->emit_load(exp);663const codegen::id result = _codegen->emit_unary_op(location, op, exp.type, value);664665exp.reset_to_rvalue(location, result, exp.type);666}667}668}669else if (accept('('))670{671// This backup may get overridden in 'accept_type_class', but should point to the same token still672backup();673674// Check if this is a C-style cast expression675if (type cast_type = {}; accept_type_class(cast_type))676{677if (peek('('))678{679// This is not a C-style cast but a constructor call, so need to roll-back and parse that instead680restore();681}682else if (expect(')'))683{684// Parse the expression behind cast operator685if (!parse_expression_unary(exp))686return false;687688// Check if the types already match, in which case there is nothing to do689if (exp.type == cast_type)690return true;691692// Check if a cast between these types is valid693if (!type::rank(exp.type, cast_type))694{695error(location, 3017, "cannot convert these types (from " + exp.type.description() + " to " + cast_type.description() + ')');696return false;697}698699exp.add_cast_operation(cast_type);700return true;701}702else703{704// Type name was not followed by a closing parenthesis705return false;706}707}708709// Parse expression between the parentheses710if (!parse_expression(exp) || !expect(')'))711return false;712}713else if (accept('{'))714{715bool is_constant = true;716std::vector<expression> elements;717type composite_type = { type::t_void, 1, 1 };718719while (!peek('}'))720{721// There should be a comma between arguments722if (!elements.empty() && !expect(','))723{724consume_until('}');725return false;726}727728// Initializer lists might contain a comma at the end, so break out of the loop if nothing follows afterwards729if (peek('}'))730break;731732expression &element_exp = elements.emplace_back();733734// Parse the argument expression735if (!parse_expression_assignment(element_exp))736{737consume_until('}');738return false;739}740741if (element_exp.type.is_array())742{743error(element_exp.location, 3119, "arrays cannot be multi-dimensional");744consume_until('}');745return false;746}747if (composite_type.base != type::t_void && element_exp.type.struct_definition != composite_type.struct_definition)748{749error(element_exp.location, 3017, "cannot convert these types (from " + element_exp.type.description() + " to " + composite_type.description() + ')');750consume_until('}');751return false;752}753754is_constant &= element_exp.is_constant; // Result is only constant if all arguments are constant755composite_type = type::merge(composite_type, element_exp.type);756}757758// Constant arrays can be constructed at compile time759if (is_constant)760{761constant result = {};762for (expression &element_exp : elements)763{764element_exp.add_cast_operation(composite_type);765result.array_data.push_back(element_exp.constant);766}767768composite_type.array_length = static_cast<unsigned int>(elements.size());769770exp.reset_to_rvalue_constant(location, std::move(result), composite_type);771}772else773{774// Resolve all access chains775for (expression &element_exp : elements)776{777element_exp.add_cast_operation(composite_type);778const codegen::id element_value = _codegen->emit_load(element_exp);779element_exp.reset_to_rvalue(element_exp.location, element_value, composite_type);780}781782composite_type.array_length = static_cast<unsigned int>(elements.size());783784const codegen::id result = _codegen->emit_construct(location, composite_type, elements);785exp.reset_to_rvalue(location, result, composite_type);786}787788return expect('}');789}790else if (accept(tokenid::true_literal))791{792exp.reset_to_rvalue_constant(location, true);793}794else if (accept(tokenid::false_literal))795{796exp.reset_to_rvalue_constant(location, false);797}798else if (accept(tokenid::int_literal))799{800exp.reset_to_rvalue_constant(location, _token.literal_as_int);801}802else if (accept(tokenid::uint_literal))803{804exp.reset_to_rvalue_constant(location, _token.literal_as_uint);805}806else if (accept(tokenid::float_literal))807{808exp.reset_to_rvalue_constant(location, _token.literal_as_float);809}810else if (accept(tokenid::double_literal))811{812// Convert double literal to float literal for now813warning(location, 5000, "double literal truncated to float literal");814815exp.reset_to_rvalue_constant(location, static_cast<float>(_token.literal_as_double));816}817else if (accept(tokenid::string_literal))818{819std::string value = std::move(_token.literal_as_string);820821// Multiple string literals in sequence are concatenated into a single string literal822while (accept(tokenid::string_literal))823value += _token.literal_as_string;824825exp.reset_to_rvalue_constant(location, std::move(value));826}827else if (type type = {}; accept_type_class(type)) // Check if this is a constructor call expression828{829if (!expect('('))830return false;831832if (!type.is_numeric())833{834error(location, 3037, "constructors only defined for numeric base types");835return false;836}837838// Empty constructors do not exist839if (accept(')'))840{841error(location, 3014, "incorrect number of arguments to numeric-type constructor");842return false;843}844845// Parse entire argument expression list846bool is_constant = true;847unsigned int num_components = 0;848std::vector<expression> arguments;849850while (!peek(')'))851{852// There should be a comma between arguments853if (!arguments.empty() && !expect(','))854return false;855856expression &argument_exp = arguments.emplace_back();857858// Parse the argument expression859if (!parse_expression_assignment(argument_exp))860return false;861862// Constructors are only defined for numeric base types863if (!argument_exp.type.is_numeric())864{865error(argument_exp.location, 3017, "cannot convert non-numeric types");866return false;867}868869is_constant &= argument_exp.is_constant; // Result is only constant if all arguments are constant870num_components += argument_exp.type.components();871}872873// The list should be terminated with a parenthesis874if (!expect(')'))875return false;876877// The total number of argument elements needs to match the number of elements in the result type878if (num_components != type.components())879{880error(location, 3014, "incorrect number of arguments to numeric-type constructor");881return false;882}883884assert(num_components > 0 && num_components <= 16 && !type.is_array());885886if (is_constant) // Constants can be converted at compile time887{888constant result = {};889unsigned int i = 0;890for (expression &argument_exp : arguments)891{892argument_exp.add_cast_operation({ type.base, argument_exp.type.rows, argument_exp.type.cols });893894for (unsigned int k = 0; k < argument_exp.type.components(); ++k)895result.as_uint[i++] = argument_exp.constant.as_uint[k];896}897898exp.reset_to_rvalue_constant(location, std::move(result), type);899}900else if (arguments.size() > 1)901{902// Flatten all arguments to a list of scalars903for (auto it = arguments.begin(); it != arguments.end();)904{905// Argument is a scalar already, so only need to cast it906if (it->type.is_scalar())907{908expression &argument_exp = *it++;909910struct type scalar_type = argument_exp.type;911scalar_type.base = type.base;912argument_exp.add_cast_operation(scalar_type);913914argument_exp.reset_to_rvalue(argument_exp.location, _codegen->emit_load(argument_exp), scalar_type);915}916else917{918const expression argument_exp = std::move(*it);919it = arguments.erase(it);920921// Convert to a scalar value and re-enter the loop in the next iteration (in case a cast is necessary too)922for (unsigned int i = argument_exp.type.components(); i > 0; --i)923{924expression argument_scalar_exp = argument_exp;925argument_scalar_exp.add_constant_index_access(i - 1);926927it = arguments.insert(it, argument_scalar_exp);928}929}930}931932const codegen::id result = _codegen->emit_construct(location, type, arguments);933934exp.reset_to_rvalue(location, result, type);935}936else // A constructor call with a single argument is identical to a cast937{938assert(!arguments.empty());939940// Reset expression to only argument and add cast to expression access chain941exp = std::move(arguments[0]); exp.add_cast_operation(type);942}943}944// At this point only identifiers are left to check and resolve945else946{947std::string identifier;948scoped_symbol symbol;949if (!accept_symbol(identifier, symbol))950return false;951952// Check if this is a function call or variable reference953if (accept('('))954{955// Can only call symbols that are functions, but do not abort yet if no symbol was found since the identifier may reference an intrinsic956if (symbol.id && symbol.op != symbol_type::function)957{958error(location, 3005, "identifier '" + identifier + "' represents a variable, not a function");959return false;960}961962// Parse entire argument expression list963std::vector<expression> arguments;964965while (!peek(')'))966{967// There should be a comma between arguments968if (!arguments.empty() && !expect(','))969return false;970971expression &argument_exp = arguments.emplace_back();972973// Parse the argument expression974if (!parse_expression_assignment(argument_exp))975return false;976}977978// The list should be terminated with a parenthesis979if (!expect(')'))980return false;981982// Function calls can only be made from within functions983if (!_codegen->is_in_function())984{985error(location, 3005, "invalid function call outside of a function");986return false;987}988989// Try to resolve the call by searching through both function symbols and intrinsics990bool undeclared = !symbol.id, ambiguous = false;991992if (!resolve_function_call(identifier, arguments, symbol.scope, symbol, ambiguous))993{994if (undeclared)995error(location, 3004, "undeclared identifier or no matching intrinsic overload for '" + identifier + '\'');996else if (ambiguous)997error(location, 3067, "ambiguous function call to '" + identifier + '\'');998else999error(location, 3013, "no matching function overload for '" + identifier + '\'');1000return false;1001}10021003assert(symbol.function != nullptr);10041005std::vector<expression> parameters(symbol.function->parameter_list.size());10061007// We need to allocate some temporary variables to pass in and load results from pointer parameters1008for (size_t i = 0; i < arguments.size(); ++i)1009{1010const auto ¶m_type = symbol.function->parameter_list[i].type;10111012if (param_type.has(type::q_out) && (!arguments[i].is_lvalue || (arguments[i].type.has(type::q_const) && !arguments[i].type.is_object())))1013{1014error(arguments[i].location, 3025, "l-value specifies const object for an 'out' parameter");1015return false;1016}10171018if (arguments[i].type.components() > param_type.components())1019warning(arguments[i].location, 3206, "implicit truncation of vector type");10201021if (symbol.op == symbol_type::function || param_type.has(type::q_out))1022{1023if (param_type.is_object() || param_type.has(type::q_groupshared) /* Special case for atomic intrinsics */)1024{1025if (arguments[i].type != param_type)1026{1027error(location, 3004, "no matching intrinsic overload for '" + identifier + '\'');1028return false;1029}10301031assert(arguments[i].is_lvalue);10321033// Do not shadow object or pointer parameters to function calls1034size_t chain_index = 0;1035const codegen::id access_chain = _codegen->emit_access_chain(arguments[i], chain_index);1036parameters[i].reset_to_lvalue(arguments[i].location, access_chain, param_type);1037assert(chain_index == arguments[i].chain.size());10381039// This is referencing a l-value, but want to avoid copying below1040parameters[i].is_lvalue = false;1041}1042else1043{1044// All user-defined functions actually accept pointers as arguments, same applies to intrinsics with 'out' parameters1045const codegen::id temp_variable = _codegen->define_variable(arguments[i].location, param_type);1046parameters[i].reset_to_lvalue(arguments[i].location, temp_variable, param_type);1047}1048}1049else1050{1051expression argument_exp = arguments[i];1052argument_exp.add_cast_operation(param_type);1053const codegen::id argument_value = _codegen->emit_load(argument_exp);1054parameters[i].reset_to_rvalue(argument_exp.location, argument_value, param_type);10551056// Keep track of whether the parameter is a constant for code generation (this makes the expression invalid for all other uses)1057parameters[i].is_constant = argument_exp.is_constant;1058}1059}10601061// Copy in parameters from the argument access chains to parameter variables1062for (size_t i = 0; i < arguments.size(); ++i)1063{1064// Only do this for pointer parameters as discovered above1065if (parameters[i].is_lvalue && parameters[i].type.has(type::q_in) && !parameters[i].type.is_object())1066{1067expression argument_exp = arguments[i];1068argument_exp.add_cast_operation(parameters[i].type);1069const codegen::id argument_value = _codegen->emit_load(argument_exp);1070_codegen->emit_store(parameters[i], argument_value);1071}1072}10731074// Add remaining default arguments1075for (size_t i = arguments.size(); i < parameters.size(); ++i)1076{1077const auto ¶m = symbol.function->parameter_list[i];1078assert(param.has_default_value || !_errors.empty());10791080const codegen::id argument_value = _codegen->emit_constant(param.type, param.default_value);1081parameters[i].reset_to_rvalue(param.location, argument_value, param.type);10821083// Keep track of whether the parameter is a constant for code generation (this makes the expression invalid for all other uses)1084parameters[i].is_constant = true;1085}10861087// Check if the call resolving found an intrinsic or function and invoke the corresponding code1088const codegen::id result = (symbol.op == symbol_type::function) ?1089_codegen->emit_call(location, symbol.id, symbol.type, parameters) :1090_codegen->emit_call_intrinsic(location, symbol.id, symbol.type, parameters);10911092exp.reset_to_rvalue(location, result, symbol.type);10931094// Copy out parameters from parameter variables back to the argument access chains1095for (size_t i = 0; i < arguments.size(); ++i)1096{1097// Only do this for pointer parameters as discovered above1098if (parameters[i].is_lvalue && parameters[i].type.has(type::q_out) && !parameters[i].type.is_object())1099{1100expression argument_exp = parameters[i];1101argument_exp.add_cast_operation(arguments[i].type);1102const codegen::id argument_value = _codegen->emit_load(argument_exp);1103_codegen->emit_store(arguments[i], argument_value);1104}1105}11061107if (_codegen->_current_function != nullptr && symbol.op == symbol_type::function)1108{1109// Calling a function makes the caller inherit all sampler and storage object references from the callee1110if (!symbol.function->referenced_samplers.empty())1111{1112std::vector<codegen::id> referenced_samplers;1113referenced_samplers.reserve(_codegen->_current_function->referenced_samplers.size() + symbol.function->referenced_samplers.size());1114std::set_union(_codegen->_current_function->referenced_samplers.begin(), _codegen->_current_function->referenced_samplers.end(), symbol.function->referenced_samplers.begin(), symbol.function->referenced_samplers.end(), std::back_inserter(referenced_samplers));1115_codegen->_current_function->referenced_samplers = std::move(referenced_samplers);1116}1117if (!symbol.function->referenced_storages.empty())1118{1119std::vector<codegen::id> referenced_storages;1120referenced_storages.reserve(_codegen->_current_function->referenced_storages.size() + symbol.function->referenced_storages.size());1121std::set_union(_codegen->_current_function->referenced_storages.begin(), _codegen->_current_function->referenced_storages.end(), symbol.function->referenced_storages.begin(), symbol.function->referenced_storages.end(), std::back_inserter(referenced_storages));1122_codegen->_current_function->referenced_storages = std::move(referenced_storages);1123}11241125// Add callee and all its function references to the callers function references1126{1127std::vector<codegen::id> referenced_functions;1128std::set_union(_codegen->_current_function->referenced_functions.begin(), _codegen->_current_function->referenced_functions.end(), symbol.function->referenced_functions.begin(), symbol.function->referenced_functions.end(), std::back_inserter(referenced_functions));1129const auto it = std::lower_bound(referenced_functions.begin(), referenced_functions.end(), symbol.id);1130if (it == referenced_functions.end() || *it != symbol.id)1131referenced_functions.insert(it, symbol.id);1132_codegen->_current_function->referenced_functions = std::move(referenced_functions);1133}1134}1135}1136else if (symbol.op == symbol_type::invalid)1137{1138// Show error if no symbol matching the identifier was found1139error(location, 3004, "undeclared identifier '" + identifier + '\'');1140return false;1141}1142else if (symbol.op == symbol_type::variable)1143{1144assert(symbol.id != 0);1145// Simply return the pointer to the variable, dereferencing is done on site where necessary1146exp.reset_to_lvalue(location, symbol.id, symbol.type);11471148if (_codegen->_current_function != nullptr &&1149symbol.scope.level == symbol.scope.namespace_level &&1150// Ignore invalid symbols that were added during error recovery1151symbol.id != 0xFFFFFFFF)1152{1153// Keep track of any global sampler or storage objects referenced in the current function1154if (symbol.type.is_sampler())1155{1156const auto it = std::lower_bound(_codegen->_current_function->referenced_samplers.begin(), _codegen->_current_function->referenced_samplers.end(), symbol.id);1157if (it == _codegen->_current_function->referenced_samplers.end() || *it != symbol.id)1158_codegen->_current_function->referenced_samplers.insert(it, symbol.id);1159}1160if (symbol.type.is_storage())1161{1162const auto it = std::lower_bound(_codegen->_current_function->referenced_storages.begin(), _codegen->_current_function->referenced_storages.end(), symbol.id);1163if (it == _codegen->_current_function->referenced_storages.end() || *it != symbol.id)1164_codegen->_current_function->referenced_storages.insert(it, symbol.id);1165}1166}1167}1168else if (symbol.op == symbol_type::constant)1169{1170// Constants are loaded into the access chain1171exp.reset_to_rvalue_constant(location, symbol.constant, symbol.type);1172}1173else1174{1175// Can only reference variables and constants by name, functions need to be called1176error(location, 3005, "identifier '" + identifier + "' represents a function, not a variable");1177return false;1178}1179}11801181while (!peek(tokenid::end_of_file))1182{1183location = _token_next.location;11841185// Check if a postfix operator exists1186if (accept_postfix_op())1187{1188// Unary operators are only valid on basic types1189if (!exp.type.is_scalar() && !exp.type.is_vector() && !exp.type.is_matrix())1190{1191error(exp.location, 3022, "scalar, vector, or matrix expected");1192return false;1193}1194if (exp.type.has(type::q_const) || !exp.is_lvalue)1195{1196error(exp.location, 3025, "l-value specifies const object");1197return false;1198}11991200// Create a constant one in the type of the expression1201const codegen::id constant_one = _codegen->emit_constant(exp.type, 1);12021203const codegen::id value = _codegen->emit_load(exp, true);1204const codegen::id result = _codegen->emit_binary_op(location, _token.id, exp.type, value, constant_one);12051206// The "++" and "--" operands modify the source variable, so store result back into it1207_codegen->emit_store(exp, result);12081209// All postfix operators return a r-value rather than a l-value to the variable1210exp.reset_to_rvalue(location, value, exp.type);1211}1212else if (accept('.'))1213{1214if (!expect(tokenid::identifier))1215return false;12161217location = std::move(_token.location);1218const std::string subscript = std::move(_token.literal_as_string);12191220if (accept('(')) // Methods (function calls on types) are not supported right now1221{1222if (!exp.type.is_struct() || exp.type.is_array())1223error(location, 3087, "object does not have methods");1224else1225error(location, 3088, "structures do not have methods");1226return false;1227}1228else if (exp.type.is_array()) // Arrays do not have subscripts1229{1230error(location, 3018, "invalid subscript on array");1231return false;1232}1233else if (exp.type.is_vector())1234{1235const int length = static_cast<int>(subscript.size());1236if (length > 4)1237{1238error(location, 3018, "invalid subscript '" + subscript + "', swizzle too long");1239return false;1240}12411242bool is_const = false;1243signed char offsets[4] = { -1, -1, -1, -1 };1244enum { xyzw, rgba, stpq } set[4];12451246for (int i = 0; i < length; ++i)1247{1248switch (subscript[i])1249{1250case 'x': offsets[i] = 0, set[i] = xyzw; break;1251case 'y': offsets[i] = 1, set[i] = xyzw; break;1252case 'z': offsets[i] = 2, set[i] = xyzw; break;1253case 'w': offsets[i] = 3, set[i] = xyzw; break;1254case 'r': offsets[i] = 0, set[i] = rgba; break;1255case 'g': offsets[i] = 1, set[i] = rgba; break;1256case 'b': offsets[i] = 2, set[i] = rgba; break;1257case 'a': offsets[i] = 3, set[i] = rgba; break;1258case 's': offsets[i] = 0, set[i] = stpq; break;1259case 't': offsets[i] = 1, set[i] = stpq; break;1260case 'p': offsets[i] = 2, set[i] = stpq; break;1261case 'q': offsets[i] = 3, set[i] = stpq; break;1262default:1263error(location, 3018, "invalid subscript '" + subscript + '\'');1264return false;1265}12661267if (i > 0 && (set[i] != set[i - 1]))1268{1269error(location, 3018, "invalid subscript '" + subscript + "', mixed swizzle sets");1270return false;1271}1272if (static_cast<unsigned int>(offsets[i]) >= exp.type.rows)1273{1274error(location, 3018, "invalid subscript '" + subscript + "', swizzle out of range");1275return false;1276}12771278// The result is not modifiable if a swizzle appears multiple times1279for (int k = 0; k < i; ++k)1280{1281if (offsets[k] == offsets[i])1282{1283is_const = true;1284break;1285}1286}1287}12881289// Add swizzle to current access chain1290exp.add_swizzle_access(offsets, static_cast<unsigned int>(length));12911292if (is_const)1293exp.type.qualifiers |= type::q_const;1294}1295else if (exp.type.is_matrix())1296{1297const int length = static_cast<int>(subscript.size());1298if (length < 3)1299{1300error(location, 3018, "invalid subscript '" + subscript + '\'');1301return false;1302}13031304bool is_const = false;1305signed char offsets[4] = { -1, -1, -1, -1 };1306const int set = subscript[1] == 'm';1307const int coefficient = !set;13081309for (int i = 0, j = 0; i < length; i += 3 + set, ++j)1310{1311if (subscript[i] != '_' ||1312subscript[i + set + 1] < '0' + coefficient ||1313subscript[i + set + 1] > '3' + coefficient ||1314subscript[i + set + 2] < '0' + coefficient ||1315subscript[i + set + 2] > '3' + coefficient)1316{1317error(location, 3018, "invalid subscript '" + subscript + '\'');1318return false;1319}1320if (set && subscript[i + 1] != 'm')1321{1322error(location, 3018, "invalid subscript '" + subscript + "', mixed swizzle sets");1323return false;1324}13251326const auto row = static_cast<unsigned int>((subscript[i + set + 1] - '0') - coefficient);1327const auto col = static_cast<unsigned int>((subscript[i + set + 2] - '0') - coefficient);13281329if ((row >= exp.type.rows || col >= exp.type.cols) || j > 3)1330{1331error(location, 3018, "invalid subscript '" + subscript + "', swizzle out of range");1332return false;1333}13341335offsets[j] = static_cast<signed char>(row * 4 + col);13361337// The result is not modifiable if a swizzle appears multiple times1338for (int k = 0; k < j; ++k)1339{1340if (offsets[k] == offsets[j])1341{1342is_const = true;1343break;1344}1345}1346}13471348// Add swizzle to current access chain1349exp.add_swizzle_access(offsets, static_cast<unsigned int>(length / (3 + set)));13501351if (is_const)1352exp.type.qualifiers |= type::q_const;1353}1354else if (exp.type.is_struct())1355{1356const std::vector<member_type> &member_list = _codegen->get_struct(exp.type.struct_definition).member_list;13571358// Find member with matching name is structure definition1359uint32_t member_index = 0;1360for (const member_type &member : member_list)1361{1362if (member.name == subscript)1363break;1364++member_index;1365}13661367if (member_index >= member_list.size())1368{1369error(location, 3018, "invalid subscript '" + subscript + '\'');1370return false;1371}13721373// Add field index to current access chain1374exp.add_member_access(member_index, member_list[member_index].type);1375}1376else if (exp.type.is_scalar())1377{1378const int length = static_cast<int>(subscript.size());1379if (length > 4)1380{1381error(location, 3018, "invalid subscript '" + subscript + "', swizzle too long");1382return false;1383}13841385for (int i = 0; i < length; ++i)1386{1387if ((subscript[i] != 'x' && subscript[i] != 'r' && subscript[i] != 's') || i > 3)1388{1389error(location, 3018, "invalid subscript '" + subscript + '\'');1390return false;1391}1392}13931394// Promote scalar to vector type using cast1395auto target_type = exp.type;1396target_type.rows = static_cast<unsigned int>(length);13971398exp.add_cast_operation(target_type);13991400if (length > 1)1401exp.type.qualifiers |= type::q_const;1402}1403else1404{1405error(location, 3018, "invalid subscript '" + subscript + '\'');1406return false;1407}1408}1409else if (accept('['))1410{1411if (!exp.type.is_array() && !exp.type.is_vector() && !exp.type.is_matrix())1412{1413error(_token.location, 3121, "array, matrix, vector, or indexable object type expected in index expression");1414return false;1415}14161417// Parse index expression1418expression index_exp;1419if (!parse_expression(index_exp) || !expect(']'))1420return false;14211422if (!index_exp.type.is_scalar() || !index_exp.type.is_integral())1423{1424error(index_exp.location, 3120, "invalid type for index - index must be an integer scalar");1425return false;1426}14271428// Add index expression to current access chain1429if (index_exp.is_constant)1430{1431// Check array bounds if known1432if (exp.type.is_bounded_array() && index_exp.constant.as_uint[0] >= exp.type.array_length)1433{1434error(index_exp.location, 3504, "array index out of bounds");1435return false;1436}14371438exp.add_constant_index_access(index_exp.constant.as_uint[0]);1439}1440else1441{1442if (exp.is_constant)1443{1444// To handle a dynamic index into a constant means we need to create a local variable first or else any of the indexing instructions do not work1445const codegen::id temp_variable = _codegen->define_variable(location, exp.type, std::string(), false, _codegen->emit_constant(exp.type, exp.constant));1446exp.reset_to_lvalue(exp.location, temp_variable, exp.type);1447}14481449exp.add_dynamic_index_access(_codegen->emit_load(index_exp));1450}1451}1452else1453{1454break;1455}1456}14571458return true;1459}14601461bool reshadefx::parser::parse_expression_multary(expression &lhs_exp, unsigned int left_precedence)1462{1463// Parse left hand side of the expression1464if (!parse_expression_unary(lhs_exp))1465return false;14661467// Check if an operator exists so that this is a binary or ternary expression1468unsigned int right_precedence;14691470while (peek_multary_op(right_precedence))1471{1472// Only process this operator if it has a lower precedence than the current operation, otherwise leave it for later and abort1473if (right_precedence <= left_precedence)1474break;14751476// Finally consume the operator token1477consume();14781479const tokenid op = _token.id;14801481// Check if this is a binary or ternary operation1482if (op != tokenid::question)1483{1484#if RESHADEFX_SHORT_CIRCUIT1485codegen::id lhs_block = 0;1486codegen::id rhs_block = 0;1487codegen::id merge_block = 0;14881489// Switch block to a new one before parsing right-hand side value in case it needs to be skipped during short-circuiting1490if (op == tokenid::ampersand_ampersand || op == tokenid::pipe_pipe)1491{1492lhs_block = _codegen->set_block(0);1493rhs_block = _codegen->create_block();1494merge_block = _codegen->create_block();14951496_codegen->enter_block(rhs_block);1497}1498#endif1499// Parse the right hand side of the binary operation1500expression rhs_exp;1501if (!parse_expression_multary(rhs_exp, right_precedence))1502return false;15031504// Deduce the result base type based on implicit conversion rules1505type type = type::merge(lhs_exp.type, rhs_exp.type);1506bool is_bool_result = false;15071508// Do some error checking depending on the operator1509if (op == tokenid::equal_equal || op == tokenid::exclaim_equal)1510{1511// Equality checks return a boolean value1512is_bool_result = true;15131514// Cannot check equality between incompatible types1515if (lhs_exp.type.is_array() || rhs_exp.type.is_array() || lhs_exp.type.struct_definition != rhs_exp.type.struct_definition)1516{1517error(rhs_exp.location, 3020, "type mismatch");1518return false;1519}1520}1521else if (op == tokenid::ampersand || op == tokenid::pipe || op == tokenid::caret)1522{1523if (type.is_boolean())1524type.base = type::t_int;15251526// Cannot perform bitwise operations on non-integral types1527if (!lhs_exp.type.is_integral())1528{1529error(lhs_exp.location, 3082, "int or unsigned int type required");1530return false;1531}1532if (!rhs_exp.type.is_integral())1533{1534error(rhs_exp.location, 3082, "int or unsigned int type required");1535return false;1536}1537}1538else1539{1540if (op == tokenid::ampersand_ampersand || op == tokenid::pipe_pipe)1541type.base = type::t_bool;1542else if (op == tokenid::less || op == tokenid::less_equal || op == tokenid::greater || op == tokenid::greater_equal)1543is_bool_result = true; // Logical operations return a boolean value1544else if (type.is_boolean())1545type.base = type::t_int; // Arithmetic with boolean values treats the operands as integers15461547// Cannot perform arithmetic operations on non-basic types1548if (!lhs_exp.type.is_scalar() && !lhs_exp.type.is_vector() && !lhs_exp.type.is_matrix())1549{1550error(lhs_exp.location, 3022, "scalar, vector, or matrix expected");1551return false;1552}1553if (!rhs_exp.type.is_scalar() && !rhs_exp.type.is_vector() && !rhs_exp.type.is_matrix())1554{1555error(rhs_exp.location, 3022, "scalar, vector, or matrix expected");1556return false;1557}1558}15591560// Perform implicit type conversion1561if (lhs_exp.type.components() > type.components())1562warning(lhs_exp.location, 3206, "implicit truncation of vector type");1563if (rhs_exp.type.components() > type.components())1564warning(rhs_exp.location, 3206, "implicit truncation of vector type");15651566lhs_exp.add_cast_operation(type);1567rhs_exp.add_cast_operation(type);15681569#if RESHADEFX_SHORT_CIRCUIT1570// Reset block to left-hand side since the load of the left-hand side value has to happen in there1571if (op == tokenid::ampersand_ampersand || op == tokenid::pipe_pipe)1572_codegen->set_block(lhs_block);1573#endif15741575// Constant expressions can be evaluated at compile time1576if (rhs_exp.is_constant && lhs_exp.evaluate_constant_expression(op, rhs_exp.constant))1577continue;15781579const codegen::id lhs_value = _codegen->emit_load(lhs_exp);15801581#if RESHADEFX_SHORT_CIRCUIT1582// Short circuit for logical && and || operators1583if (op == tokenid::ampersand_ampersand || op == tokenid::pipe_pipe)1584{1585// Emit "if ( lhs) result = rhs" for && expression1586codegen::id condition_value = lhs_value;1587// Emit "if (!lhs) result = rhs" for || expression1588if (op == tokenid::pipe_pipe)1589condition_value = _codegen->emit_unary_op(lhs_exp.location, tokenid::exclaim, type, lhs_value);15901591_codegen->leave_block_and_branch_conditional(condition_value, rhs_block, merge_block);15921593_codegen->set_block(rhs_block);1594// Only load value of right hand side expression after entering the second block1595const codegen::id rhs_value = _codegen->emit_load(rhs_exp);1596_codegen->leave_block_and_branch(merge_block);15971598_codegen->enter_block(merge_block);15991600const codegen::id result_value = _codegen->emit_phi(lhs_exp.location, condition_value, lhs_block, rhs_value, rhs_block, lhs_value, lhs_block, type);16011602lhs_exp.reset_to_rvalue(lhs_exp.location, result_value, type);1603continue;1604}1605#endif1606const codegen::id rhs_value = _codegen->emit_load(rhs_exp);16071608// Certain operations return a boolean type instead of the type of the input expressions1609if (is_bool_result)1610type = { type::t_bool, type.rows, type.cols };16111612const codegen::id result_value = _codegen->emit_binary_op(lhs_exp.location, op, type, lhs_exp.type, lhs_value, rhs_value);16131614lhs_exp.reset_to_rvalue(lhs_exp.location, result_value, type);1615}1616else1617{1618// A conditional expression needs a scalar or vector type condition1619if (!lhs_exp.type.is_scalar() && !lhs_exp.type.is_vector())1620{1621error(lhs_exp.location, 3022, "boolean or vector expression expected");1622return false;1623}16241625#if RESHADEFX_SHORT_CIRCUIT1626// Switch block to a new one before parsing first part in case it needs to be skipped during short-circuiting1627const codegen::id merge_block = _codegen->create_block();1628const codegen::id condition_block = _codegen->set_block(0);1629codegen::id true_block = _codegen->create_block();1630codegen::id false_block = _codegen->create_block();16311632_codegen->enter_block(true_block);1633#endif1634// Parse the first part of the right hand side of the ternary operation1635expression true_exp;1636if (!parse_expression(true_exp))1637return false;16381639if (!expect(':'))1640return false;16411642#if RESHADEFX_SHORT_CIRCUIT1643// Switch block to a new one before parsing second part in case it needs to be skipped during short-circuiting1644_codegen->set_block(0);1645_codegen->enter_block(false_block);1646#endif1647// Parse the second part of the right hand side of the ternary operation1648expression false_exp;1649if (!parse_expression_assignment(false_exp))1650return false;16511652// Check that the condition dimension matches that of at least one side1653if (lhs_exp.type.rows != true_exp.type.rows && lhs_exp.type.cols != true_exp.type.cols)1654{1655error(lhs_exp.location, 3020, "dimension of conditional does not match value");1656return false;1657}16581659// Check that the two value expressions can be converted between each other1660if (true_exp.type.array_length != false_exp.type.array_length || true_exp.type.struct_definition != false_exp.type.struct_definition)1661{1662error(false_exp.location, 3020, "type mismatch between conditional values");1663return false;1664}16651666// Deduce the result base type based on implicit conversion rules1667const type type = type::merge(true_exp.type, false_exp.type);16681669if (true_exp.type.components() > type.components())1670warning(true_exp.location, 3206, "implicit truncation of vector type");1671if (false_exp.type.components() > type.components())1672warning(false_exp.location, 3206, "implicit truncation of vector type");16731674#if RESHADEFX_SHORT_CIRCUIT1675// Reset block to left-hand side since the load of the condition value has to happen in there1676_codegen->set_block(condition_block);1677#else1678// The conditional operator instruction expects the condition to be a boolean type1679lhs_exp.add_cast_operation({ type::t_bool, type.rows, 1 });1680#endif1681true_exp.add_cast_operation(type);1682false_exp.add_cast_operation(type);16831684// Load condition value from expression1685const codegen::id condition_value = _codegen->emit_load(lhs_exp);16861687#if RESHADEFX_SHORT_CIRCUIT1688_codegen->leave_block_and_branch_conditional(condition_value, true_block, false_block);16891690_codegen->set_block(true_block);1691// Only load true expression value after entering the first block1692const codegen::id true_value = _codegen->emit_load(true_exp);1693true_block = _codegen->leave_block_and_branch(merge_block);16941695_codegen->set_block(false_block);1696// Only load false expression value after entering the second block1697const codegen::id false_value = _codegen->emit_load(false_exp);1698false_block = _codegen->leave_block_and_branch(merge_block);16991700_codegen->enter_block(merge_block);17011702const codegen::id result_value = _codegen->emit_phi(lhs_exp.location, condition_value, condition_block, true_value, true_block, false_value, false_block, type);1703#else1704const codegen::id true_value = _codegen->emit_load(true_exp);1705const codegen::id false_value = _codegen->emit_load(false_exp);17061707const codegen::id result_value = _codegen->emit_ternary_op(lhs_exp.location, op, type, condition_value, true_value, false_value);1708#endif1709lhs_exp.reset_to_rvalue(lhs_exp.location, result_value, type);1710}1711}17121713return true;1714}17151716bool reshadefx::parser::parse_expression_assignment(expression &lhs_exp)1717{1718// Parse left hand side of the expression1719if (!parse_expression_multary(lhs_exp))1720return false;17211722// Check if an operator exists so that this is an assignment1723if (accept_assignment_op())1724{1725// Remember the operator token before parsing the expression that follows it1726const tokenid op = _token.id;17271728// Parse right hand side of the assignment expression1729// This may be another assignment expression to support chains like "a = b = c = 0;"1730expression rhs_exp;1731if (!parse_expression_assignment(rhs_exp))1732return false;17331734// Check if the assignment is valid1735if (lhs_exp.type.has(type::q_const) || !lhs_exp.is_lvalue)1736{1737error(lhs_exp.location, 3025, "l-value specifies const object");1738return false;1739}1740if (!type::rank(lhs_exp.type, rhs_exp.type))1741{1742error(rhs_exp.location, 3020, "cannot convert these types (from " + rhs_exp.type.description() + " to " + lhs_exp.type.description() + ')');1743return false;1744}17451746// Cannot perform bitwise operations on non-integral types1747if (!lhs_exp.type.is_integral() && (op == tokenid::ampersand_equal || op == tokenid::pipe_equal || op == tokenid::caret_equal))1748{1749error(lhs_exp.location, 3082, "int or unsigned int type required");1750return false;1751}17521753// Perform implicit type conversion of right hand side value1754if (rhs_exp.type.components() > lhs_exp.type.components())1755warning(rhs_exp.location, 3206, "implicit truncation of vector type");17561757rhs_exp.add_cast_operation(lhs_exp.type);17581759codegen::id result_value = _codegen->emit_load(rhs_exp);17601761// Check if this is an assignment with an additional arithmetic instruction1762if (op != tokenid::equal)1763{1764// Load value for modification1765const codegen::id lhs_value = _codegen->emit_load(lhs_exp);17661767// Handle arithmetic assignment operation1768result_value = _codegen->emit_binary_op(lhs_exp.location, op, lhs_exp.type, lhs_value, result_value);1769}17701771// Write result back to variable1772_codegen->emit_store(lhs_exp, result_value);17731774// Return the result value since you can write assignments within expressions1775lhs_exp.reset_to_rvalue(lhs_exp.location, result_value, lhs_exp.type);1776}17771778return true;1779}178017811782