blob: 7392343e20203cdddaf90817090fd6fdbb513950 [file] [log] [blame]
/*
* Copyright (C) 2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "WHLSLLexer.h"
#if ENABLE(WEBGPU)
namespace WebCore {
namespace WHLSL {
const char* Token::typeName(Type type)
{
switch (type) {
case Type::IntLiteral:
return "int literal";
case Type::UintLiteral:
return "uint literal";
case Type::FloatLiteral:
return "float literal";
case Type::Struct:
return "struct";
case Type::Typedef:
return "typedef";
case Type::Enum:
return "enum";
case Type::Operator:
return "operator";
case Type::If:
return "if";
case Type::Else:
return "else";
case Type::Continue:
return "continue";
case Type::Break:
return "break";
case Type::Switch:
return "switch";
case Type::Case:
return "case";
case Type::Default:
return "default";
case Type::Fallthrough:
return "fallthrough";
case Type::For:
return "for";
case Type::While:
return "while";
case Type::Do:
return "do";
case Type::Return:
return "return";
case Type::Null:
return "null";
case Type::True:
return "true";
case Type::False:
return "false";
case Type::Constant:
return "constant";
case Type::Device:
return "device";
case Type::Threadgroup:
return "threadgroup";
case Type::Thread:
return "thread";
case Type::Space:
return "space";
case Type::Vertex:
return "vertex";
case Type::Fragment:
return "fragment";
case Type::Compute:
return "compute";
case Type::NumThreads:
return "numthreads";
case Type::SVInstanceID:
return "SV_InstanceID";
case Type::SVVertexID:
return "SV_VertexID";
case Type::PSize:
return "PSIZE";
case Type::SVPosition:
return "SV_Position";
case Type::SVIsFrontFace:
return "SV_IsFrontFace";
case Type::SVSampleIndex:
return "SV_SampleIndex";
case Type::SVInnerCoverage:
return "SV_InnerCoverage";
case Type::SVTarget:
return "SV_Target";
case Type::SVDepth:
return "SV_Depth";
case Type::SVCoverage:
return "SV_Coverage";
case Type::SVDispatchThreadID:
return "SV_DispatchThreadID";
case Type::SVGroupID:
return "SV_GroupID";
case Type::SVGroupIndex:
return "SV_GroupIndex";
case Type::SVGroupThreadID:
return "SV_GroupThreadID";
case Type::Attribute:
return "SV_Attribute";
case Type::Register:
return "register";
case Type::Specialized:
return "specialized";
case Type::Native:
return "native";
case Type::Restricted:
return "restricted";
case Type::Underscore:
return "_";
case Type::Auto:
return "auto";
case Type::Protocol:
return "protocol";
case Type::Const:
return "const";
case Type::Static:
return "static";
case Type::Qualifier:
return "qualifier";
case Type::Identifier:
return "identifier";
case Type::OperatorName:
return "operator name";
case Type::EqualsSign:
return "=";
case Type::Semicolon:
return ";";
case Type::LeftCurlyBracket:
return "{";
case Type::RightCurlyBracket:
return "}";
case Type::Colon:
return ":";
case Type::Comma:
return ",";
case Type::LeftParenthesis:
return "(";
case Type::RightParenthesis:
return ")";
case Type::SquareBracketPair:
return "[]";
case Type::LeftSquareBracket:
return "[";
case Type::RightSquareBracket:
return "]";
case Type::Star:
return "*";
case Type::LessThanSign:
return "<";
case Type::GreaterThanSign:
return ">";
case Type::FullStop:
return ".";
case Type::PlusEquals:
return "+=";
case Type::MinusEquals:
return "-=";
case Type::TimesEquals:
return "*=";
case Type::DivideEquals:
return "/=";
case Type::ModEquals:
return "%=";
case Type::XorEquals:
return "^=";
case Type::AndEquals:
return "&=";
case Type::OrEquals:
return "|=";
case Type::RightShiftEquals:
return ">>=";
case Type::LeftShiftEquals:
return "<<=";
case Type::PlusPlus:
return "++";
case Type::MinusMinus:
return "--";
case Type::Arrow:
return "->";
case Type::QuestionMark:
return "?";
case Type::OrOr:
return "||";
case Type::AndAnd:
return "&&";
case Type::Or:
return "|";
case Type::Xor:
return "^";
case Type::And:
return "&";
case Type::LessThanOrEqualTo:
return "<=";
case Type::GreaterThanOrEqualTo:
return ">=";
case Type::EqualComparison:
return "==";
case Type::NotEqual:
return "!=";
case Type::RightShift:
return ">>";
case Type::LeftShift:
return "<<";
case Type::Plus:
return "+";
case Type::Minus:
return "-";
case Type::Divide:
return "/";
case Type::Mod:
return "%";
case Type::Tilde:
return "~";
case Type::ExclamationPoint:
return "!";
case Type::At:
return "@";
case Type::EndOfFile:
return "EOF";
case Type::Invalid:
return "LEXING_ERROR";
}
}
static ALWAYS_INLINE bool isValidIdentifierStart(UChar theChar)
{
return (theChar >= 'a' && theChar <= 'z')
|| (theChar >= 'A' && theChar <= 'Z')
|| (theChar == '_');
}
static ALWAYS_INLINE bool isValidNonStartingIdentifierChar(UChar theChar)
{
return (theChar >= 'a' && theChar <= 'z')
|| (theChar >= 'A' && theChar <= 'Z')
|| (theChar >= '0' && theChar <= '9')
|| (theChar == '_');
}
static ALWAYS_INLINE bool isHexadecimalCharacter(UChar character)
{
return (character >= '0' && character <= '9')
|| (character >= 'a' && character <= 'f')
|| (character >= 'A' && character <= 'F');
}
static ALWAYS_INLINE bool isDigit(UChar theChar)
{
return theChar >= '0' && theChar <= '9';
}
auto Lexer::consumeTokenFromStream() -> Token
{
UChar current = 0;
unsigned offset = m_offset;
auto peek = [&] () -> UChar {
if (offset < m_stringView.length())
return m_stringView[offset];
return 0;
};
auto shift = [&] {
if (offset < m_stringView.length()) {
current = m_stringView[offset];
++offset;
return current;
}
current = 0;
return current;
};
auto consume = [&] (UChar theChar) {
if (peek() == theChar) {
shift();
return true;
}
return false;
};
auto nextIsIdentifier = [&] {
return isValidNonStartingIdentifierChar(peek());
};
auto token = [&] (Token::Type type) -> Token {
auto oldOffset = m_offset;
m_offset = offset;
skipWhitespaceAndComments();
return { { oldOffset, offset, m_nameSpace }, type };
};
switch (shift()) {
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'Q':
case 'R':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
case 'g':
case 'h':
case 'j':
case 'k':
case 'l':
case 'm':
case 'q':
case 'x':
case 'y':
case 'z':
parseIdentifier:
while (isValidNonStartingIdentifierChar(peek()))
shift();
return token(Token::Type::Identifier);
case 's':
switch (peek()) {
case 'a':
shift();
if (!consume('m'))
goto parseIdentifier;
if (!consume('p'))
goto parseIdentifier;
if (!consume('l'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Qualifier);
case 'w':
shift();
if (!consume('i'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('h'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Switch);
case 't':
shift();
switch (peek()) {
case 'r':
shift();
if (!consume('u'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Struct);
case 'a':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Static);
default:
goto parseIdentifier;
}
break;
case 'p':
shift();
switch (peek()) {
case 'a':
shift();
if (!consume('c'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Space);
case 'e':
shift();
if (!consume('c'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('l'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('z'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Specialized);
default:
goto parseIdentifier;
}
default:
goto parseIdentifier;
}
case 'S':
if (!consume('V'))
goto parseIdentifier;
if (!consume('_'))
goto parseIdentifier;
switch (peek()) {
case 'G':
shift();
if (!consume('r'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('u'))
goto parseIdentifier;
if (!consume('p'))
goto parseIdentifier;
switch (peek()) {
case 'I':
shift();
switch (peek()) {
case 'D':
shift();
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVGroupID);
case 'n':
shift();
if (!consume('d'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('x'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVGroupIndex);
default:
goto parseIdentifier;
}
case 'T':
shift();
if (!consume('h'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (!consume('I'))
goto parseIdentifier;
if (!consume('D'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVGroupThreadID);
default:
goto parseIdentifier;
}
case 'D':
shift();
switch (peek()) {
case 'e':
shift();
if (!consume('p'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('h'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVDepth);
case 'i':
shift();
if (!consume('s'))
goto parseIdentifier;
if (!consume('p'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('h'))
goto parseIdentifier;
if (!consume('T'))
goto parseIdentifier;
if (!consume('h'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (!consume('I'))
goto parseIdentifier;
if (!consume('D'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVDispatchThreadID);
default:
goto parseIdentifier;
}
case 'C':
shift();
if (!consume('o'))
goto parseIdentifier;
if (!consume('v'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('g'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVCoverage);
case 'T':
shift();
if (!consume('a'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('g'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVTarget);
case 'S':
shift();
if (!consume('a'))
goto parseIdentifier;
if (!consume('m'))
goto parseIdentifier;
if (!consume('p'))
goto parseIdentifier;
if (!consume('l'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('I'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('x'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVSampleIndex);
case 'P':
shift();
if (!consume('o'))
goto parseIdentifier;
if (!consume('s'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVPosition);
case 'V':
shift();
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('x'))
goto parseIdentifier;
if (!consume('I'))
goto parseIdentifier;
if (!consume('D'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVVertexID);
case 'I':
shift();
switch (peek()) {
case 's':
shift();
if (!consume('F'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('F'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVIsFrontFace);
case 'n':
shift();
switch (peek()) {
case 'n':
shift();
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('C'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('v'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('g'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVInnerCoverage);
case 's':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('I'))
goto parseIdentifier;
if (!consume('D'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::SVInstanceID);
default:
goto parseIdentifier;
}
default:
goto parseIdentifier;
}
default:
goto parseIdentifier;
}
case 't':
switch (peek()) {
case 'r':
shift();
if (!consume('u'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::True);
case 'y':
shift();
if (!consume('p'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('f'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Typedef);
case 'h':
shift();
if (!consume('r'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (!nextIsIdentifier())
return token(Token::Type::Thread);
if (peek() != 'g')
goto parseIdentifier;
shift();
RELEASE_ASSERT(current == 'g');
if (!consume('r'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('u'))
goto parseIdentifier;
if (!consume('p'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Threadgroup);
default:
goto parseIdentifier;
}
case 'e':
switch (peek()) {
case 'n':
shift();
if (!consume('u'))
goto parseIdentifier;
if (!consume('m'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Enum);
case 'l':
shift();
if (!consume('s'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Else);
default:
goto parseIdentifier;
}
case 'o':
if (!consume('p'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
switch (peek()) {
case '&':
shift();
return token(Token::Type::OperatorName);
case '>':
shift();
if (consume('>'))
return token(Token::Type::OperatorName);
if (consume('='))
return token(Token::Type::OperatorName);
return token(Token::Type::OperatorName);
case '<':
shift();
if (consume('<'))
return token(Token::Type::OperatorName);
if (consume('='))
return token(Token::Type::OperatorName);
return token(Token::Type::OperatorName);
case '+':
shift();
consume('+');
return token(Token::Type::OperatorName);
case '-':
shift();
consume('-');
return token(Token::Type::OperatorName);
case '|':
shift();
return token(Token::Type::OperatorName);
case '=':
shift();
if (!consume('='))
return token(Token::Type::Invalid);
return token(Token::Type::OperatorName);
case '*':
shift();
return token(Token::Type::OperatorName);
case '/':
shift();
return token(Token::Type::OperatorName);
case '%':
shift();
return token(Token::Type::OperatorName);
case '!':
shift();
return token(Token::Type::OperatorName);
case '~':
shift();
return token(Token::Type::OperatorName);
case '^':
shift();
return token(Token::Type::OperatorName);
default:
break;
}
return token(Token::Type::Operator);
case 'i':
if (!consume('f'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::If);
case 'c':
switch (peek()) {
case 'a':
shift();
if (!consume('s'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Case);
case 'e':
shift();
if (!consume('n'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Qualifier);
case 'o':
shift();
switch (peek()) {
case 'm':
shift();
if (!consume('p'))
goto parseIdentifier;
if (!consume('u'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Compute);
case 'n':
shift();
switch (peek()) {
case 't':
shift();
if (!consume('i'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (!consume('u'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Continue);
case 's':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!nextIsIdentifier())
return token(Token::Type::Const);
if (!consume('a'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Constant);
default:
goto parseIdentifier;
}
default:
goto parseIdentifier;
}
default:
goto parseIdentifier;
}
case 'b':
if (!consume('r'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('k'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Break);
case 'd':
switch (peek()) {
case 'o':
shift();
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Do);
case 'e':
shift();
switch (peek()) {
case 'f':
shift();
if (!consume('a'))
goto parseIdentifier;
if (!consume('u'))
goto parseIdentifier;
if (!consume('l'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Default);
case 'v':
shift();
if (!consume('i'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Device);
default:
goto parseIdentifier;
}
default:
goto parseIdentifier;
}
case 'f':
switch (peek()) {
case 'o':
shift();
if (!consume('r'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::For);
case 'r':
shift();
if (!consume('a'))
goto parseIdentifier;
if (!consume('g'))
goto parseIdentifier;
if (!consume('m'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Fragment);
case 'a':
shift();
if (!consume('l'))
goto parseIdentifier;
switch (peek()) {
case 'l':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!consume('h'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('u'))
goto parseIdentifier;
if (!consume('g'))
goto parseIdentifier;
if (!consume('h'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Fallthrough);
case 's':
shift();
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::False);
default:
goto parseIdentifier;
}
case 'e':
shift();
switch (peek()) {
case 'f':
shift();
if (!consume('a'))
goto parseIdentifier;
if (!consume('u'))
goto parseIdentifier;
if (!consume('l'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Default);
case 'v':
shift();
if (!consume('i'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Device);
default:
goto parseIdentifier;
}
default:
goto parseIdentifier;
}
case 'w':
if (!consume('h'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('l'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::While);
case 'r':
if (!consume('e'))
goto parseIdentifier;
switch (peek()) {
case 't':
shift();
if (!consume('u'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Return);
case 'g':
shift();
if (!consume('i'))
goto parseIdentifier;
if (!consume('s'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Register);
case 's':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Restricted);
default:
goto parseIdentifier;
}
case 'n':
switch (peek()) {
case 'a':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('v'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Native);
case 'u':
shift();
switch (peek()) {
case 'l':
shift();
if (!consume('l'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Null);
case 'm':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!consume('h'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('d'))
goto parseIdentifier;
if (!consume('s'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::NumThreads);
default:
goto parseIdentifier;
}
case 'o':
shift();
switch (peek()) {
case 'i':
shift();
if (!consume('n'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('p'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('l'))
goto parseIdentifier;
if (!consume('a'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('n'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Qualifier);
case 'p':
shift();
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('s'))
goto parseIdentifier;
if (!consume('p'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('v'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Qualifier);
default:
goto parseIdentifier;
}
default:
goto parseIdentifier;
}
case 'v':
if (!consume('e'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (!consume('x'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Vertex);
case 'P':
if (!consume('S'))
goto parseIdentifier;
if (!consume('I'))
goto parseIdentifier;
if (!consume('Z'))
goto parseIdentifier;
if (!consume('E'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::PSize);
case 'u':
if (!consume('n'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('f'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('m'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Qualifier);
case 'p':
if (!consume('r'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('c'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (!consume('l'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Protocol);
case '_':
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Underscore);
case 'a':
switch (peek()) {
case 't':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!consume('r'))
goto parseIdentifier;
if (!consume('i'))
goto parseIdentifier;
if (!consume('b'))
goto parseIdentifier;
if (!consume('u'))
goto parseIdentifier;
if (!consume('t'))
goto parseIdentifier;
if (!consume('e'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Attribute);
case 'u':
shift();
if (!consume('t'))
goto parseIdentifier;
if (!consume('o'))
goto parseIdentifier;
if (nextIsIdentifier())
goto parseIdentifier;
return token(Token::Type::Auto);
default:
goto parseIdentifier;
}
case '>':
if (consume('>')) {
if (consume('='))
return token(Token::Type::RightShiftEquals);
return token(Token::Type::RightShift);
}
if (consume('='))
return token(Token::Type::GreaterThanOrEqualTo);
return token(Token::Type::GreaterThanSign);
case '<':
if (consume('<')) {
if (consume('='))
return token(Token::Type::LeftShiftEquals);
return token(Token::Type::LeftShift);
}
if (consume('='))
return token(Token::Type::LessThanOrEqualTo);
return token(Token::Type::LessThanSign);
case '+':
if (consume('='))
return token(Token::Type::PlusEquals);
if (consume('+'))
return token(Token::Type::PlusPlus);
return token(Token::Type::Plus);
case '-':
if (consume('='))
return token(Token::Type::MinusEquals);
if (consume('-'))
return token(Token::Type::MinusMinus);
if (consume('>'))
return token(Token::Type::Arrow);
if (isDigit(peek())) {
shift();
goto parseNumber;
}
if (consume('.'))
goto parseFloatAfterDot;
return token(Token::Type::Minus);
case '*':
if (consume('='))
return token(Token::Type::TimesEquals);
return token(Token::Type::Star);
case '/':
if (consume('='))
return token(Token::Type::DivideEquals);
return token(Token::Type::Divide);
case '%':
if (consume('='))
return token(Token::Type::ModEquals);
return token(Token::Type::Mod);
case '^':
if (consume('='))
return token(Token::Type::XorEquals);
return token(Token::Type::Xor);
case '&':
if (consume('='))
return token(Token::Type::AndEquals);
if (consume('&'))
return token(Token::Type::AndAnd);
return token(Token::Type::And);
case '|':
if (consume('='))
return token(Token::Type::OrEquals);
if (consume('|'))
return token(Token::Type::OrOr);
return token(Token::Type::Or);
case '[':
if (consume(']'))
return token(Token::Type::SquareBracketPair);
return token(Token::Type::LeftSquareBracket);
case '=':
if (consume('='))
return token(Token::Type::EqualComparison);
return token(Token::Type::EqualsSign);
case '!':
if (consume('='))
return token(Token::Type::NotEqual);
return token(Token::Type::ExclamationPoint);
case ';':
return token(Token::Type::Semicolon);
case '{':
return token(Token::Type::LeftCurlyBracket);
case '}':
return token(Token::Type::RightCurlyBracket);
case ':':
return token(Token::Type::Colon);
case ',':
return token(Token::Type::Comma);
case '(':
return token(Token::Type::LeftParenthesis);
case ')':
return token(Token::Type::RightParenthesis);
case ']':
return token(Token::Type::RightSquareBracket);
case '.':
if (isDigit(peek()))
goto parseFloatAfterDot;
return token(Token::Type::FullStop);
case '?':
return token(Token::Type::QuestionMark);
case '~':
return token(Token::Type::Tilde);
case '@':
return token(Token::Type::At);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
parseNumber:
if (current == '0' && consume('x')) {
while (isHexadecimalCharacter(peek()))
shift();
if (consume('u'))
return token(Token::Type::UintLiteral);
return token(Token::Type::IntLiteral);
}
while (isDigit(peek()))
shift();
if (consume('.')) {
parseFloatAfterDot:
while (isDigit(peek()))
shift();
consume('f');
return token(Token::Type::FloatLiteral);
}
if (consume('f'))
return token(Token::Type::FloatLiteral);
if (consume('u'))
return token(Token::Type::UintLiteral);
return token(Token::Type::IntLiteral);
}
default:
break;
}
if (m_offset == m_stringView.length())
return token(Token::Type::EndOfFile);
return token(Token::Type::Invalid);
}
// We can take advantage of two properties of Unicode:
// 1. The consitutent UTF-16 code units for all non-BMP code points are surrogates,
// which means we'll never see a false match. If we see a BMP code unit, we
// really have a BMP code point.
// 2. Everything we're looking for is in BMP
static inline bool isWhitespace(UChar codeUnit)
{
switch (codeUnit) {
case ' ':
case '\t':
case '\r':
case '\n':
return true;
default:
return false;
}
}
static inline bool isNewline(UChar codeUnit)
{
switch (codeUnit) {
case '\r':
case '\n':
return true;
default:
return false;
}
}
auto Lexer::lineAndColumnNumberFromOffset(const StringView& stringView, unsigned targetOffset) -> LineAndColumn
{
// Counting from 1 to match most text editors.
unsigned lineNumber = 1;
unsigned columnNumber = 1;
for (unsigned offset = 0; offset < std::min(stringView.length(), targetOffset); ++offset) {
++columnNumber;
if (isNewline(stringView[offset])) {
++lineNumber;
columnNumber = 1;
}
}
return { lineNumber, columnNumber };
}
static Optional<StringView> sourceFromNameSpace(AST::NameSpace nameSpace, const String& source1, const String* source2)
{
switch (nameSpace) {
case AST::NameSpace::StandardLibrary:
return WTF::nullopt;
case AST::NameSpace::NameSpace1:
return StringView(source1);
case AST::NameSpace::NameSpace2:
ASSERT(source2);
return StringView(*source2);
}
}
String Lexer::errorString(Error error, const String& source1, const String* source2)
{
if (auto codeLocation = error.codeLocation()) {
if (auto source = sourceFromNameSpace(codeLocation.nameSpace(), source1, source2)) {
auto lineAndColumn = lineAndColumnNumberFromOffset(*source, error.codeLocation().startOffset());
return makeString(lineAndColumn.line, ':', lineAndColumn.column, ": ", error.message());
}
}
return error.message();
}
void Lexer::skipWhitespaceAndComments()
{
while (m_offset < m_stringView.length()) {
if (isWhitespace(m_stringView[m_offset]))
++m_offset;
else if (m_stringView[m_offset] == '/' && m_offset + 1 < m_stringView.length()) {
if (m_stringView[m_offset + 1] == '/') {
// Line comment
m_offset += 2;
// Note that in the case of \r\n this makes the comment end on the \r. It is fine, as the \n after that is simple whitespace.
for ( ; m_offset < m_stringView.length() && !isNewline(m_stringView[m_offset]); ++m_offset) { }
} else if (m_stringView[m_offset + 1] == '*') {
// Long comment
for ( ; m_offset < m_stringView.length() ; ++m_offset) {
if (m_stringView[m_offset] == '*' && m_offset + 1 < m_stringView.length() && m_stringView[m_offset + 1] == '/') {
m_offset += 2;
break;
}
}
} else
break;
} else
break;
}
}
} // namespace WHLSL
} // namespace WebCore
#endif // ENABLE(WEBGPU)