Path: blob/master/dep/reshadefx/src/effect_lexer.cpp
4246 views
/*1* Copyright (C) 2014 Patrick Mours2* SPDX-License-Identifier: BSD-3-Clause3*/45#include "effect_lexer.hpp"6#include <cassert>7#include <string_view>8#include <unordered_map> // Used for static lookup tables910using namespace reshadefx;1112enum token_type13{14DIGIT = '0',15IDENT = 'A',16SPACE = ' ',17};1819// Lookup table which translates a given char to a token type20static const unsigned int s_type_lookup[256] = {210xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, SPACE,22'\n', SPACE, SPACE, SPACE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,230x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,240x00, 0x00, SPACE, '!', '"', '#', '$', '%', '&', '\'',25'(', ')', '*', '+', ',', '-', '.', '/', DIGIT, DIGIT,26DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, ':', ';',27'<', '=', '>', '?', '@', IDENT, IDENT, IDENT, IDENT, IDENT,28IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT,29IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT,30IDENT, '[', '\\', ']', '^', IDENT, 0x00, IDENT, IDENT, IDENT,31IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT,32IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT, IDENT,33IDENT, IDENT, IDENT, '{', '|', '}', '~', 0x00, 0x00, 0x00,34};3536// Lookup tables which translate a given string literal to a token and backwards37static const std::unordered_map<tokenid, std::string_view> s_token_lookup = {38{ tokenid::end_of_file, "end of file" },39{ tokenid::exclaim, "!" },40{ tokenid::hash, "#" },41{ tokenid::dollar, "$" },42{ tokenid::percent, "%" },43{ tokenid::ampersand, "&" },44{ tokenid::parenthesis_open, "(" },45{ tokenid::parenthesis_close, ")" },46{ tokenid::star, "*" },47{ tokenid::plus, "+" },48{ tokenid::comma, "," },49{ tokenid::minus, "-" },50{ tokenid::dot, "." },51{ tokenid::slash, "/" },52{ tokenid::colon, ":" },53{ tokenid::semicolon, ";" },54{ tokenid::less, "<" },55{ tokenid::equal, "=" },56{ tokenid::greater, ">" },57{ tokenid::question, "?" },58{ tokenid::at, "@" },59{ tokenid::bracket_open, "[" },60{ tokenid::backslash, "\\" },61{ tokenid::bracket_close, "]" },62{ tokenid::caret, "^" },63{ tokenid::brace_open, "{" },64{ tokenid::pipe, "|" },65{ tokenid::brace_close, "}" },66{ tokenid::tilde, "~" },67{ tokenid::exclaim_equal, "!=" },68{ tokenid::percent_equal, "%=" },69{ tokenid::ampersand_ampersand, "&&" },70{ tokenid::ampersand_equal, "&=" },71{ tokenid::star_equal, "*=" },72{ tokenid::plus_plus, "++" },73{ tokenid::plus_equal, "+=" },74{ tokenid::minus_minus, "--" },75{ tokenid::minus_equal, "-=" },76{ tokenid::arrow, "->" },77{ tokenid::ellipsis, "..." },78{ tokenid::slash_equal, "|=" },79{ tokenid::colon_colon, "::" },80{ tokenid::less_less_equal, "<<=" },81{ tokenid::less_less, "<<" },82{ tokenid::less_equal, "<=" },83{ tokenid::equal_equal, "==" },84{ tokenid::greater_greater_equal, ">>=" },85{ tokenid::greater_greater, ">>" },86{ tokenid::greater_equal, ">=" },87{ tokenid::caret_equal, "^=" },88{ tokenid::pipe_equal, "|=" },89{ tokenid::pipe_pipe, "||" },90{ tokenid::identifier, "identifier" },91{ tokenid::reserved, "reserved word" },92{ tokenid::true_literal, "true" },93{ tokenid::false_literal, "false" },94{ tokenid::int_literal, "integral literal" },95{ tokenid::uint_literal, "integral literal" },96{ tokenid::float_literal, "floating point literal" },97{ tokenid::double_literal, "floating point literal" },98{ tokenid::string_literal, "string literal" },99{ tokenid::namespace_, "namespace" },100{ tokenid::struct_, "struct" },101{ tokenid::technique, "technique" },102{ tokenid::pass, "pass" },103{ tokenid::for_, "for" },104{ tokenid::while_, "while" },105{ tokenid::do_, "do" },106{ tokenid::if_, "if" },107{ tokenid::else_, "else" },108{ tokenid::switch_, "switch" },109{ tokenid::case_, "case" },110{ tokenid::default_, "default" },111{ tokenid::break_, "break" },112{ tokenid::continue_, "continue" },113{ tokenid::return_, "return" },114{ tokenid::discard_, "discard" },115{ tokenid::extern_, "extern" },116{ tokenid::static_, "static" },117{ tokenid::uniform_, "uniform" },118{ tokenid::volatile_, "volatile" },119{ tokenid::precise, "precise" },120{ tokenid::groupshared, "groupshared" },121{ tokenid::in, "in" },122{ tokenid::out, "out" },123{ tokenid::inout, "inout" },124{ tokenid::const_, "const" },125{ tokenid::linear, "linear" },126{ tokenid::noperspective, "noperspective" },127{ tokenid::centroid, "centroid" },128{ tokenid::nointerpolation, "nointerpolation" },129{ tokenid::void_, "void" },130{ tokenid::bool_, "bool" },131{ tokenid::bool2, "bool2" },132{ tokenid::bool3, "bool3" },133{ tokenid::bool4, "bool4" },134{ tokenid::bool2x2, "bool2x2" },135{ tokenid::bool2x3, "bool2x3" },136{ tokenid::bool2x4, "bool2x4" },137{ tokenid::bool3x2, "bool3x2" },138{ tokenid::bool3x3, "bool3x3" },139{ tokenid::bool3x4, "bool3x4" },140{ tokenid::bool4x2, "bool4x2" },141{ tokenid::bool4x3, "bool4x3" },142{ tokenid::bool4x4, "bool4x4" },143{ tokenid::int_, "int" },144{ tokenid::int2, "int2" },145{ tokenid::int3, "int3" },146{ tokenid::int4, "int4" },147{ tokenid::int2x2, "int2x2" },148{ tokenid::int2x3, "int2x3" },149{ tokenid::int2x4, "int2x4" },150{ tokenid::int3x2, "int3x2" },151{ tokenid::int3x3, "int3x3" },152{ tokenid::int3x4, "int3x4" },153{ tokenid::int4x2, "int4x2" },154{ tokenid::int4x3, "int4x3" },155{ tokenid::int4x4, "int4x4" },156{ tokenid::min16int, "min16int" },157{ tokenid::min16int2, "min16int2" },158{ tokenid::min16int3, "min16int3" },159{ tokenid::min16int4, "min16int4" },160{ tokenid::uint_, "uint" },161{ tokenid::uint2, "uint2" },162{ tokenid::uint3, "uint3" },163{ tokenid::uint4, "uint4" },164{ tokenid::uint2x2, "uint2x2" },165{ tokenid::uint2x3, "uint2x3" },166{ tokenid::uint2x4, "uint2x4" },167{ tokenid::uint3x2, "uint3x2" },168{ tokenid::uint3x3, "uint3x3" },169{ tokenid::uint3x4, "uint3x4" },170{ tokenid::uint4x2, "uint4x2" },171{ tokenid::uint4x3, "uint4x3" },172{ tokenid::uint4x4, "uint4x4" },173{ tokenid::min16uint, "min16uint" },174{ tokenid::min16uint2, "min16uint2" },175{ tokenid::min16uint3, "min16uint3" },176{ tokenid::min16uint4, "min16uint4" },177{ tokenid::float_, "float" },178{ tokenid::float2, "float2" },179{ tokenid::float3, "float3" },180{ tokenid::float4, "float4" },181{ tokenid::float2x2, "float2x2" },182{ tokenid::float2x3, "float2x3" },183{ tokenid::float2x4, "float2x4" },184{ tokenid::float3x2, "float3x2" },185{ tokenid::float3x3, "float3x3" },186{ tokenid::float3x4, "float3x4" },187{ tokenid::float4x2, "float4x2" },188{ tokenid::float4x3, "float4x3" },189{ tokenid::float4x4, "float4x4" },190{ tokenid::min16float, "min16float" },191{ tokenid::min16float2, "min16float2" },192{ tokenid::min16float3, "min16float3" },193{ tokenid::min16float4, "min16float4" },194{ tokenid::vector, "vector" },195{ tokenid::matrix, "matrix" },196{ tokenid::string_, "string" },197{ tokenid::texture1d, "texture1D" },198{ tokenid::texture2d, "texture2D" },199{ tokenid::texture3d, "texture3D" },200{ tokenid::sampler1d, "sampler1D" },201{ tokenid::sampler2d, "sampler2D" },202{ tokenid::sampler3d, "sampler3D" },203{ tokenid::storage1d, "storage1D" },204{ tokenid::storage2d, "storage2D" },205{ tokenid::storage3d, "storage3D" },206};207static const std::unordered_map<std::string_view, tokenid> s_keyword_lookup = {208{ "asm", tokenid::reserved },209{ "asm_fragment", tokenid::reserved },210{ "auto", tokenid::reserved },211{ "bool", tokenid::bool_ },212{ "bool2", tokenid::bool2 },213{ "bool2x1", tokenid::bool2 },214{ "bool2x2", tokenid::bool2x2 },215{ "bool2x3", tokenid::bool2x3 },216{ "bool2x4", tokenid::bool2x4 },217{ "bool3", tokenid::bool3 },218{ "bool3x1", tokenid::bool3 },219{ "bool3x2", tokenid::bool3x2 },220{ "bool3x3", tokenid::bool3x3 },221{ "bool3x4", tokenid::bool3x4 },222{ "bool4", tokenid::bool4 },223{ "bool4x1", tokenid::bool4 },224{ "bool4x2", tokenid::bool4x2 },225{ "bool4x3", tokenid::bool4x3 },226{ "bool4x4", tokenid::bool4x4 },227{ "break", tokenid::break_ },228{ "case", tokenid::case_ },229{ "cast", tokenid::reserved },230{ "catch", tokenid::reserved },231{ "centroid", tokenid::reserved },232{ "char", tokenid::reserved },233{ "class", tokenid::reserved },234{ "column_major", tokenid::reserved },235{ "compile", tokenid::reserved },236{ "const", tokenid::const_ },237{ "const_cast", tokenid::reserved },238{ "continue", tokenid::continue_ },239{ "default", tokenid::default_ },240{ "delete", tokenid::reserved },241{ "discard", tokenid::discard_ },242{ "do", tokenid::do_ },243{ "double", tokenid::reserved },244{ "dword", tokenid::uint_ },245{ "dword2", tokenid::uint2 },246{ "dword2x1", tokenid::uint2 },247{ "dword2x2", tokenid::uint2x2 },248{ "dword2x3", tokenid::uint2x3 },249{ "dword2x4", tokenid::uint2x4 },250{ "dword3", tokenid::uint3, },251{ "dword3x1", tokenid::uint3 },252{ "dword3x2", tokenid::uint3x2 },253{ "dword3x3", tokenid::uint3x3 },254{ "dword3x4", tokenid::uint3x4 },255{ "dword4", tokenid::uint4 },256{ "dword4x1", tokenid::uint4 },257{ "dword4x2", tokenid::uint4x2 },258{ "dword4x3", tokenid::uint4x3 },259{ "dword4x4", tokenid::uint4x4 },260{ "dynamic_cast", tokenid::reserved },261{ "else", tokenid::else_ },262{ "enum", tokenid::reserved },263{ "explicit", tokenid::reserved },264{ "extern", tokenid::extern_ },265{ "external", tokenid::reserved },266{ "false", tokenid::false_literal },267{ "FALSE", tokenid::false_literal },268{ "float", tokenid::float_ },269{ "float2", tokenid::float2 },270{ "float2x1", tokenid::float2 },271{ "float2x2", tokenid::float2x2 },272{ "float2x3", tokenid::float2x3 },273{ "float2x4", tokenid::float2x4 },274{ "float3", tokenid::float3 },275{ "float3x1", tokenid::float3 },276{ "float3x2", tokenid::float3x2 },277{ "float3x3", tokenid::float3x3 },278{ "float3x4", tokenid::float3x4 },279{ "float4", tokenid::float4 },280{ "float4x1", tokenid::float4 },281{ "float4x2", tokenid::float4x2 },282{ "float4x3", tokenid::float4x3 },283{ "float4x4", tokenid::float4x4 },284{ "for", tokenid::for_ },285{ "foreach", tokenid::reserved },286{ "friend", tokenid::reserved },287{ "globallycoherent", tokenid::reserved },288{ "goto", tokenid::reserved },289{ "groupshared", tokenid::groupshared },290{ "half", tokenid::reserved },291{ "half2", tokenid::reserved },292{ "half2x1", tokenid::reserved },293{ "half2x2", tokenid::reserved },294{ "half2x3", tokenid::reserved },295{ "half2x4", tokenid::reserved },296{ "half3", tokenid::reserved },297{ "half3x1", tokenid::reserved },298{ "half3x2", tokenid::reserved },299{ "half3x3", tokenid::reserved },300{ "half3x4", tokenid::reserved },301{ "half4", tokenid::reserved },302{ "half4x1", tokenid::reserved },303{ "half4x2", tokenid::reserved },304{ "half4x3", tokenid::reserved },305{ "half4x4", tokenid::reserved },306{ "if", tokenid::if_ },307{ "in", tokenid::in },308{ "inline", tokenid::reserved },309{ "inout", tokenid::inout },310{ "int", tokenid::int_ },311{ "int2", tokenid::int2 },312{ "int2x1", tokenid::int2 },313{ "int2x2", tokenid::int2x2 },314{ "int2x3", tokenid::int2x3 },315{ "int2x4", tokenid::int2x4 },316{ "int3", tokenid::int3 },317{ "int3x1", tokenid::int3 },318{ "int3x2", tokenid::int3x2 },319{ "int3x3", tokenid::int3x3 },320{ "int3x4", tokenid::int3x4 },321{ "int4", tokenid::int4 },322{ "int4x1", tokenid::int4 },323{ "int4x2", tokenid::int4x2 },324{ "int4x3", tokenid::int4x3 },325{ "int4x4", tokenid::int4x4 },326{ "interface", tokenid::reserved },327{ "linear", tokenid::linear },328{ "long", tokenid::reserved },329{ "matrix", tokenid::matrix },330{ "min16float", tokenid::min16float },331{ "min16float2", tokenid::min16float2 },332{ "min16float3", tokenid::min16float3 },333{ "min16float4", tokenid::min16float4 },334{ "min16int", tokenid::min16int },335{ "min16int2", tokenid::min16int2 },336{ "min16int3", tokenid::min16int3 },337{ "min16int4", tokenid::min16int4 },338{ "min16uint", tokenid::min16uint },339{ "min16uint2", tokenid::min16uint2 },340{ "min16uint3", tokenid::min16uint3 },341{ "min16uint4", tokenid::min16uint4 },342{ "mutable", tokenid::reserved },343{ "namespace", tokenid::namespace_ },344{ "new", tokenid::reserved },345{ "noinline", tokenid::reserved },346{ "nointerpolation", tokenid::nointerpolation },347{ "noperspective", tokenid::noperspective },348{ "operator", tokenid::reserved },349{ "out", tokenid::out },350{ "packed", tokenid::reserved },351{ "packoffset", tokenid::reserved },352{ "pass", tokenid::pass },353{ "precise", tokenid::precise },354{ "private", tokenid::reserved },355{ "protected", tokenid::reserved },356{ "public", tokenid::reserved },357{ "register", tokenid::reserved },358{ "reinterpret_cast", tokenid::reserved },359{ "restrict", tokenid::reserved },360{ "return", tokenid::return_ },361{ "row_major", tokenid::reserved },362{ "sample", tokenid::reserved },363{ "sampler", tokenid::sampler2d },364{ "sampler1D", tokenid::sampler1d },365{ "sampler1DArray", tokenid::reserved },366{ "sampler2D", tokenid::sampler2d },367{ "sampler2DArray", tokenid::reserved },368{ "sampler2DMS", tokenid::reserved },369{ "sampler2DMSArray", tokenid::reserved },370{ "sampler3D", tokenid::sampler3d },371{ "sampler_state", tokenid::reserved },372{ "samplerCube", tokenid::reserved },373{ "samplerCubeArray", tokenid::reserved },374{ "samplerCUBE", tokenid::reserved },375{ "samplerRect", tokenid::reserved },376{ "samplerRECT", tokenid::reserved },377{ "SamplerState", tokenid::reserved },378{ "storage", tokenid::storage2d },379{ "storage1D", tokenid::storage1d },380{ "storage2D", tokenid::storage2d },381{ "storage3D", tokenid::storage3d },382{ "shared", tokenid::reserved },383{ "short", tokenid::reserved },384{ "signed", tokenid::reserved },385{ "sizeof", tokenid::reserved },386{ "snorm", tokenid::reserved },387{ "static", tokenid::static_ },388{ "static_cast", tokenid::reserved },389{ "string", tokenid::string_ },390{ "struct", tokenid::struct_ },391{ "switch", tokenid::switch_ },392{ "technique", tokenid::technique },393{ "template", tokenid::reserved },394{ "texture", tokenid::texture2d },395{ "Texture1D", tokenid::reserved },396{ "texture1D", tokenid::texture1d },397{ "Texture1DArray", tokenid::reserved },398{ "Texture2D", tokenid::reserved },399{ "texture2D", tokenid::texture2d },400{ "Texture2DArray", tokenid::reserved },401{ "Texture2DMS", tokenid::reserved },402{ "Texture2DMSArray", tokenid::reserved },403{ "Texture3D", tokenid::reserved },404{ "texture3D", tokenid::texture3d },405{ "textureCUBE", tokenid::reserved },406{ "TextureCube", tokenid::reserved },407{ "TextureCubeArray", tokenid::reserved },408{ "textureRECT", tokenid::reserved },409{ "this", tokenid::reserved },410{ "true", tokenid::true_literal },411{ "TRUE", tokenid::true_literal },412{ "try", tokenid::reserved },413{ "typedef", tokenid::reserved },414{ "uint", tokenid::uint_ },415{ "uint2", tokenid::uint2 },416{ "uint2x1", tokenid::uint2 },417{ "uint2x2", tokenid::uint2x2 },418{ "uint2x3", tokenid::uint2x3 },419{ "uint2x4", tokenid::uint2x4 },420{ "uint3", tokenid::uint3 },421{ "uint3x1", tokenid::uint3 },422{ "uint3x2", tokenid::uint3x2 },423{ "uint3x3", tokenid::uint3x3 },424{ "uint3x4", tokenid::uint3x4 },425{ "uint4", tokenid::uint4 },426{ "uint4x1", tokenid::uint4 },427{ "uint4x2", tokenid::uint4x2 },428{ "uint4x3", tokenid::uint4x3 },429{ "uint4x4", tokenid::uint4x4 },430{ "uniform", tokenid::uniform_ },431{ "union", tokenid::reserved },432{ "unorm", tokenid::reserved },433{ "unsigned", tokenid::reserved },434{ "using", tokenid::reserved },435{ "vector", tokenid::vector },436{ "virtual", tokenid::reserved },437{ "void", tokenid::void_ },438{ "volatile", tokenid::volatile_ },439{ "while", tokenid::while_ }440};441static const std::unordered_map<std::string_view, tokenid> s_pp_directive_lookup = {442{ "define", tokenid::hash_def },443{ "undef", tokenid::hash_undef },444{ "if", tokenid::hash_if },445{ "ifdef", tokenid::hash_ifdef },446{ "ifndef", tokenid::hash_ifndef },447{ "else", tokenid::hash_else },448{ "elif", tokenid::hash_elif },449{ "endif", tokenid::hash_endif },450{ "error", tokenid::hash_error },451{ "warning", tokenid::hash_warning },452{ "pragma", tokenid::hash_pragma },453{ "include", tokenid::hash_include },454};455456static bool is_octal_digit(char c)457{458return static_cast<unsigned>(c - '0') < 8;459}460static bool is_decimal_digit(char c)461{462return static_cast<unsigned>(c - '0') < 10;463}464static bool is_hexadecimal_digit(char c)465{466return is_decimal_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');467}468469static bool is_digit(char c, int radix)470{471switch (radix)472{473case 8:474return is_octal_digit(c);475case 10:476return is_decimal_digit(c);477case 16:478return is_hexadecimal_digit(c);479}480481return false;482}483static long long octal_to_decimal(long long n)484{485long long m = 0;486487while (n != 0)488{489m *= 8;490m += n & 7;491n >>= 3;492}493494while (m != 0)495{496n *= 10;497n += m & 7;498m >>= 3;499}500501return n;502}503504std::string reshadefx::token::id_to_name(tokenid id)505{506const auto it = s_token_lookup.find(id);507if (it != s_token_lookup.end())508return std::string(it->second);509return "unknown";510}511512reshadefx::token reshadefx::lexer::lex()513{514bool is_at_line_begin = _cur_location.column <= 1;515516token tok;517next_token:518// Reset token data519tok.location = _cur_location;520tok.offset = input_offset();521tok.length = 1;522tok.literal_as_double = 0;523tok.literal_as_string.clear();524525assert(_cur <= _end);526527// Do a character type lookup for the current character528switch (s_type_lookup[uint8_t(*_cur)])529{530case 0xFF: // EOF531tok.id = tokenid::end_of_file;532return tok;533case SPACE:534skip_space();535if (_ignore_whitespace || is_at_line_begin || *_cur == '\n')536goto next_token;537tok.id = tokenid::space;538tok.length = input_offset() - tok.offset;539return tok;540case '\n':541_cur++;542_cur_location.line++;543_cur_location.column = 1;544is_at_line_begin = true;545if (_ignore_whitespace)546goto next_token;547tok.id = tokenid::end_of_line;548return tok;549case DIGIT:550parse_numeric_literal(tok);551break;552case IDENT:553parse_identifier(tok);554break;555case '!':556if (_cur[1] == '=')557tok.id = tokenid::exclaim_equal,558tok.length = 2;559else560tok.id = tokenid::exclaim;561break;562case '"':563parse_string_literal(tok, _escape_string_literals);564break;565case '#':566if (is_at_line_begin)567{568if (!parse_pp_directive(tok) || _ignore_pp_directives)569{570skip_to_next_line();571goto next_token;572}573} // These braces are important so the 'else' is matched to the right 'if' statement574else575tok.id = tokenid::hash;576break;577case '$':578tok.id = tokenid::dollar;579break;580case '%':581if (_cur[1] == '=')582tok.id = tokenid::percent_equal,583tok.length = 2;584else585tok.id = tokenid::percent;586break;587case '&':588if (_cur[1] == '&')589tok.id = tokenid::ampersand_ampersand,590tok.length = 2;591else if (_cur[1] == '=')592tok.id = tokenid::ampersand_equal,593tok.length = 2;594else595tok.id = tokenid::ampersand;596break;597case '(':598tok.id = tokenid::parenthesis_open;599break;600case ')':601tok.id = tokenid::parenthesis_close;602break;603case '*':604if (_cur[1] == '=')605tok.id = tokenid::star_equal,606tok.length = 2;607else608tok.id = tokenid::star;609break;610case '+':611if (_cur[1] == '+')612tok.id = tokenid::plus_plus,613tok.length = 2;614else if (_cur[1] == '=')615tok.id = tokenid::plus_equal,616tok.length = 2;617else618tok.id = tokenid::plus;619break;620case ',':621tok.id = tokenid::comma;622break;623case '-':624if (_cur[1] == '-')625tok.id = tokenid::minus_minus,626tok.length = 2;627else if (_cur[1] == '=')628tok.id = tokenid::minus_equal,629tok.length = 2;630else if (_cur[1] == '>')631tok.id = tokenid::arrow,632tok.length = 2;633else634tok.id = tokenid::minus;635break;636case '.':637if (s_type_lookup[uint8_t(_cur[1])] == DIGIT)638parse_numeric_literal(tok);639else if (_cur[1] == '.' && _cur[2] == '.')640tok.id = tokenid::ellipsis,641tok.length = 3;642else643tok.id = tokenid::dot;644break;645case '/':646if (_cur[1] == '/')647{648skip_to_next_line();649if (_ignore_comments)650goto next_token;651tok.id = tokenid::single_line_comment;652tok.length = input_offset() - tok.offset;653return tok;654}655else if (_cur[1] == '*')656{657while (_cur < _end)658{659if (*_cur == '\n')660{661_cur_location.line++;662_cur_location.column = 1;663}664else if (_cur[0] == '*' && _cur[1] == '/')665{666skip(2);667break;668}669skip(1);670}671if (_ignore_comments)672goto next_token;673tok.id = tokenid::multi_line_comment;674tok.length = input_offset() - tok.offset;675return tok;676}677else if (_cur[1] == '=')678tok.id = tokenid::slash_equal,679tok.length = 2;680else681tok.id = tokenid::slash;682break;683case ':':684if (_cur[1] == ':')685tok.id = tokenid::colon_colon,686tok.length = 2;687else688tok.id = tokenid::colon;689break;690case ';':691tok.id = tokenid::semicolon;692break;693case '<':694if (_cur[1] == '<')695if (_cur[2] == '=')696tok.id = tokenid::less_less_equal,697tok.length = 3;698else699tok.id = tokenid::less_less,700tok.length = 2;701else if (_cur[1] == '=')702tok.id = tokenid::less_equal,703tok.length = 2;704else705tok.id = tokenid::less;706break;707case '=':708if (_cur[1] == '=')709tok.id = tokenid::equal_equal,710tok.length = 2;711else712tok.id = tokenid::equal;713break;714case '>':715if (_cur[1] == '>')716if (_cur[2] == '=')717tok.id = tokenid::greater_greater_equal,718tok.length = 3;719else720tok.id = tokenid::greater_greater,721tok.length = 2;722else if (_cur[1] == '=')723tok.id = tokenid::greater_equal,724tok.length = 2;725else726tok.id = tokenid::greater;727break;728case '?':729tok.id = tokenid::question;730break;731case '@':732tok.id = tokenid::at;733break;734case '[':735tok.id = tokenid::bracket_open;736break;737case '\\':738if (_cur[1] == '\n' || (_cur[1] == '\r' && _cur[2] == '\n'))739{740// Skip to next line if current line ends with a backslash741skip_space();742if (_ignore_whitespace)743goto next_token;744tok.id = tokenid::space;745tok.length = input_offset() - tok.offset;746return tok;747}748tok.id = tokenid::backslash;749break;750case ']':751tok.id = tokenid::bracket_close;752break;753case '^':754if (_cur[1] == '=')755tok.id = tokenid::caret_equal,756tok.length = 2;757else758tok.id = tokenid::caret;759break;760case '{':761tok.id = tokenid::brace_open;762break;763case '|':764if (_cur[1] == '=')765tok.id = tokenid::pipe_equal,766tok.length = 2;767else if (_cur[1] == '|')768tok.id = tokenid::pipe_pipe,769tok.length = 2;770else771tok.id = tokenid::pipe;772break;773case '}':774tok.id = tokenid::brace_close;775break;776case '~':777tok.id = tokenid::tilde;778break;779default:780tok.id = tokenid::unknown;781break;782}783784skip(tok.length);785786return tok;787}788789void reshadefx::lexer::skip(size_t length)790{791_cur += length;792_cur_location.column += static_cast<unsigned int>(length);793}794void reshadefx::lexer::skip_space()795{796// Skip each character until a space is found797while (_cur < _end)798{799if (_cur[0] == '\\' && (_cur[1] == '\n' || (_cur[1] == '\r' && _cur[2] == '\n')))800{801skip(_cur[1] == '\r' ? 3 : 2);802_cur_location.line++;803_cur_location.column = 1;804continue;805}806807if (s_type_lookup[uint8_t(*_cur)] == SPACE)808skip(1);809else810break;811}812}813void reshadefx::lexer::skip_to_next_line()814{815// Skip each character until a new line feed is found816while (*_cur != '\n' && _cur < _end)817{818#if 0819if (_cur[0] == '\\' && (_cur[1] == '\n' || (_cur[1] == '\r' && _cur[2] == '\n')))820{821skip(_cur[1] == '\r' ? 3 : 2);822_cur_location.line++;823_cur_location.column = 1;824continue;825}826#endif827828skip(1);829}830}831832void reshadefx::lexer::reset_to_offset(size_t offset)833{834assert(offset < _input.size());835_cur = _input.data() + offset;836}837838void reshadefx::lexer::parse_identifier(token &tok) const839{840auto *const begin = _cur, *end = begin;841842// Skip to the end of the identifier sequence843while (s_type_lookup[uint8_t(*end)] == IDENT || s_type_lookup[uint8_t(*end)] == DIGIT)844end++;845846tok.id = tokenid::identifier;847tok.offset = input_offset();848tok.length = end - begin;849tok.literal_as_string.assign(begin, end);850851if (_ignore_keywords)852return;853854if (const auto it = s_keyword_lookup.find(tok.literal_as_string);855it != s_keyword_lookup.end())856tok.id = it->second;857}858bool reshadefx::lexer::parse_pp_directive(token &tok)859{860skip(1); // Skip the '#'861skip_space(); // Skip any space between the '#' and directive862parse_identifier(tok);863864if (const auto it = s_pp_directive_lookup.find(tok.literal_as_string);865it != s_pp_directive_lookup.end())866{867tok.id = it->second;868return true;869}870else if (!_ignore_line_directives && tok.literal_as_string == "line") // The #line directive needs special handling871{872skip(tok.length); // The 'parse_identifier' does not update the pointer to the current character, so do that now873skip_space();874parse_numeric_literal(tok);875skip(tok.length);876877_cur_location.line = tok.literal_as_int;878879// Need to subtract one since the line containing #line does not count into the statistics880if (_cur_location.line != 0)881_cur_location.line--;882883skip_space();884885// Check if this #line directive has an file name attached to it886if (_cur[0] == '"')887{888token temptok;889parse_string_literal(temptok, false);890891_cur_location.source = std::move(temptok.literal_as_string);892}893894// Do not return the #line directive as token to the caller895return false;896}897898tok.id = tokenid::hash_unknown;899900return true;901}902void reshadefx::lexer::parse_string_literal(token &tok, bool escape)903{904auto *const begin = _cur, *end = begin + 1; // Skip first quote character right away905906for (auto c = *end; c != '"'; c = *++end)907{908if (c == '\n' || end >= _end)909{910// Line feed reached, the string literal is done (technically this should be an error, but the lexer does not report errors, so ignore it)911end--;912if (end[0] == '\r') end--;913break;914}915916if (c == '\r')917{918// Silently ignore carriage return characters919continue;920}921922if (unsigned int n = (end[1] == '\r' && end + 2 < _end) ? 2 : 1;923c == '\\' && end[n] == '\n')924{925// Escape character found at end of line, the string literal continues on to the next line926end += n;927_cur_location.line++;928continue;929}930931// Handle escape sequences932if (c == '\\' && escape)933{934unsigned int n = 0;935936// Any character following the '\' is not parsed as usual, so increment pointer here (this makes sure '\"' does not abort the outer loop as well)937switch (c = *++end)938{939case '0':940case '1':941case '2':942case '3':943case '4':944case '5':945case '6':946case '7':947for (unsigned int i = 0; i < 3 && is_octal_digit(*end) && end < _end; i++)948{949c = *end++;950n = (n << 3) | (c - '0');951}952// For simplicity the number is limited to what fits in a single character953c = n & 0xFF;954// The octal parsing loop above incremented one pass the escape sequence, so step back955end--;956break;957case 'a':958c = '\a';959break;960case 'b':961c = '\b';962break;963case 'f':964c = '\f';965break;966case 'n':967c = '\n';968break;969case 'r':970c = '\r';971break;972case 't':973c = '\t';974break;975case 'v':976c = '\v';977break;978case 'x':979if (is_hexadecimal_digit(*++end))980{981while (is_hexadecimal_digit(*end) && end < _end)982{983c = *end++;984n = (n << 4) | (is_decimal_digit(c) ? (c - '0') : (c - 55 - 32 * (c & 0x20)));985}986987// For simplicity the number is limited to what fits in a single character988c = n & 0xFF;989}990// The hexadecimal parsing loop and check above incremented one pass the escape sequence, so step back991end--;992break;993}994}995996tok.literal_as_string += c;997}998999tok.id = tokenid::string_literal;1000tok.length = end - begin + 1;10011002// Free up unused memory1003tok.literal_as_string.shrink_to_fit();1004}1005void reshadefx::lexer::parse_numeric_literal(token &tok) const1006{1007// This routine handles both integer and floating point numbers1008auto *const begin = _cur, *end = _cur;1009int mantissa_size = 0, decimal_location = -1, radix = 10;1010long long fraction = 0, exponent = 0;10111012// If a literal starts with '0' it is either an octal or hexadecimal ('0x') value1013if (begin[0] == '0')1014{1015if (begin[1] == 'x' || begin[1] == 'X')1016{1017end = begin + 2;1018radix = 16;1019}1020else1021{1022radix = 8;1023}1024}10251026for (; mantissa_size <= 18; mantissa_size++, end++)1027{1028auto c = *end;10291030if (is_decimal_digit(c))1031{1032c -= '0';10331034if (c >= radix)1035break;1036}1037else if (radix == 16)1038{1039// Hexadecimal values can contain the letters A to F1040if (c >= 'A' && c <= 'F')1041c -= 'A' - 10;1042else if (c >= 'a' && c <= 'f')1043c -= 'a' - 10;1044else1045break;1046}1047else1048{1049if (c != '.' || decimal_location >= 0)1050break;10511052// Found a decimal character, as such convert current values1053if (radix == 8)1054{1055radix = 10;1056fraction = octal_to_decimal(fraction);1057}10581059decimal_location = mantissa_size;1060continue;1061}10621063fraction *= radix;1064fraction += c;1065}10661067// Ignore additional digits that cannot affect the value1068while (is_digit(*end, radix))1069end++;10701071// If a decimal character was found, this is a floating point value, otherwise an integer one1072if (decimal_location < 0)1073{1074tok.id = tokenid::int_literal;1075decimal_location = mantissa_size;1076}1077else1078{1079tok.id = tokenid::float_literal;1080mantissa_size -= 1;1081}10821083// Literals can be followed by an exponent1084if (*end == 'E' || *end == 'e')1085{1086auto tmp = end + 1;1087const bool negative = *tmp == '-';10881089if (negative || *tmp == '+')1090tmp++;10911092if (is_decimal_digit(*tmp))1093{1094end = tmp;10951096tok.id = tokenid::float_literal;10971098do {1099exponent *= 10;1100exponent += (*end++) - '0';1101} while (is_decimal_digit(*end));11021103if (negative)1104exponent = -exponent;1105}1106}11071108// Various suffixes force specific literal types1109if (*end == 'F' || *end == 'f')1110{1111end++; // Consume the suffix1112tok.id = tokenid::float_literal;1113}1114else if (*end == 'L' || *end == 'l')1115{1116end++; // Consume the suffix1117tok.id = tokenid::double_literal;1118}1119else if (tok.id == tokenid::int_literal && (*end == 'U' || *end == 'u')) // The 'u' suffix is only valid on integers and needs to be ignored otherwise1120{1121end++; // Consume the suffix1122tok.id = tokenid::uint_literal;1123}11241125if (tok.id == tokenid::float_literal || tok.id == tokenid::double_literal)1126{1127exponent += decimal_location - mantissa_size;11281129const bool exponent_negative = exponent < 0;11301131if (exponent_negative)1132exponent = -exponent;11331134// Limit exponent1135if (exponent > 511)1136exponent = 511;11371138// Quick exponent calculation1139double e = 1.0;1140const double powers_of_10[] = {114110.,1142100.,11431.0e4,11441.0e8,11451.0e16,11461.0e32,11471.0e64,11481.0e128,11491.0e2561150};11511152for (auto d = powers_of_10; exponent != 0; exponent >>= 1, d++)1153if (exponent & 1)1154e *= *d;11551156if (tok.id == tokenid::float_literal)1157tok.literal_as_float = exponent_negative ? fraction / static_cast<float>(e) : fraction * static_cast<float>(e);1158else1159tok.literal_as_double = exponent_negative ? fraction / e : fraction * e;1160}1161else1162{1163// Limit the maximum value to what fits into our token structure1164tok.literal_as_uint = static_cast<unsigned int>(fraction & 0xFFFFFFFF);1165}11661167tok.length = end - begin;1168}116911701171