LibGLSL: Add initial GLSL parser implementation
This commit is contained in:
parent
0b6424d883
commit
29972876e4
Notes:
sideshowbarker
2024-07-17 09:37:30 +09:00
Author: https://github.com/Poseydon42 Commit: https://github.com/SerenityOS/serenity/commit/29972876e4 Pull-request: https://github.com/SerenityOS/serenity/pull/20168 Reviewed-by: https://github.com/Hendiadyoin1 Reviewed-by: https://github.com/gmta ✅ Reviewed-by: https://github.com/sunverwerth Reviewed-by: https://github.com/vkoskiv
11 changed files with 4093 additions and 0 deletions
517
Userland/Libraries/LibGLSL/AST.cpp
Normal file
517
Userland/Libraries/LibGLSL/AST.cpp
Normal file
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
* Copyright (c) 2023, Volodymyr V. <vvmposeydon@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "AST.h"
|
||||
|
||||
namespace GLSL {
|
||||
|
||||
static ErrorOr<void> print_indent(AK::Stream& output, int indent)
|
||||
{
|
||||
for (int i = 0; i < indent * 2; ++i)
|
||||
TRY(output.write_some(" "sv.bytes()));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> ASTNode::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(print_indent(output, indent));
|
||||
TRY(output.write_formatted("{}[{}:{}->{}:{}]\n", class_name(), start().line, start().column, end().line, end().column));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> TranslationUnit::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
for (auto const& child : m_declarations) {
|
||||
TRY(child->dump(output, indent + 1));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> FunctionDeclaration::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
|
||||
TRY(m_return_type->dump(output, indent + 1));
|
||||
|
||||
if (!m_name.is_null()) {
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("{}\n", m_name->name()));
|
||||
}
|
||||
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("(\n"));
|
||||
|
||||
for (auto const& arg : m_parameters) {
|
||||
TRY(arg->dump(output, indent + 1));
|
||||
}
|
||||
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted(")\n"));
|
||||
|
||||
if (!m_definition.is_null()) {
|
||||
TRY(m_definition->dump(output, indent + 1));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Vector<NonnullRefPtr<Declaration const>> FunctionDeclaration::declarations() const
|
||||
{
|
||||
Vector<NonnullRefPtr<Declaration const>> declarations;
|
||||
for (auto& arg : m_parameters) {
|
||||
declarations.append(arg);
|
||||
}
|
||||
|
||||
if (m_definition)
|
||||
declarations.extend(m_definition->declarations());
|
||||
|
||||
return declarations;
|
||||
}
|
||||
|
||||
ErrorOr<void> Type::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
|
||||
StringBuilder qualifiers_string;
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Const).has_value())
|
||||
qualifiers_string.append("const "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::In).has_value())
|
||||
qualifiers_string.append("in "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Out).has_value())
|
||||
qualifiers_string.append("out "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Inout).has_value())
|
||||
qualifiers_string.append("inout "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Centroid).has_value())
|
||||
qualifiers_string.append("centroid "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Patch).has_value())
|
||||
qualifiers_string.append("patch "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Sample).has_value())
|
||||
qualifiers_string.append("sample "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Uniform).has_value())
|
||||
qualifiers_string.append("uniform "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Buffer).has_value())
|
||||
qualifiers_string.append("buffer "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Shared).has_value())
|
||||
qualifiers_string.append("shared "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Coherent).has_value())
|
||||
qualifiers_string.append("coherent "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Volatile).has_value())
|
||||
qualifiers_string.append("volatile "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Restrict).has_value())
|
||||
qualifiers_string.append("restrict "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Readonly).has_value())
|
||||
qualifiers_string.append("readonly "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Writeonly).has_value())
|
||||
qualifiers_string.append("writeonly "sv);
|
||||
if (m_storage_qualifiers.find_first_index(StorageTypeQualifier::Subroutine).has_value())
|
||||
qualifiers_string.append("subroutine "sv);
|
||||
TRY(output.write_formatted("{}{}\n", qualifiers_string.string_view(), m_name->name()));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> Parameter::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
if (!m_name.is_null()) {
|
||||
TRY(print_indent(output, indent));
|
||||
TRY(output.write_formatted("{}\n", m_name->name()));
|
||||
}
|
||||
if (m_type)
|
||||
TRY(m_type->dump(output, indent + 1));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> FunctionDefinition::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(print_indent(output, indent));
|
||||
TRY(output.write_formatted("{{\n"));
|
||||
for (auto const& statement : m_statements) {
|
||||
TRY(statement->dump(output, indent + 1));
|
||||
}
|
||||
TRY(print_indent(output, indent));
|
||||
TRY(output.write_formatted("}}\n"));
|
||||
return {};
|
||||
}
|
||||
|
||||
Vector<NonnullRefPtr<Declaration const>> FunctionDefinition::declarations() const
|
||||
{
|
||||
Vector<NonnullRefPtr<Declaration const>> declarations;
|
||||
for (auto& statement : m_statements) {
|
||||
declarations.extend(statement->declarations());
|
||||
}
|
||||
return declarations;
|
||||
}
|
||||
|
||||
ErrorOr<void> VariableDeclaration::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
if (m_type)
|
||||
TRY(m_type->dump(output, indent + 1));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("{}\n", m_name->name()));
|
||||
if (m_initial_value)
|
||||
TRY(m_initial_value->dump(output, indent + 1));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> NumericLiteral::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("{}\n", m_value));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> BinaryExpression::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
|
||||
char const* op_string = nullptr;
|
||||
switch (m_op) {
|
||||
case BinaryOp::Addition:
|
||||
op_string = "+";
|
||||
break;
|
||||
case BinaryOp::Subtraction:
|
||||
op_string = "-";
|
||||
break;
|
||||
case BinaryOp::Multiplication:
|
||||
op_string = "*";
|
||||
break;
|
||||
case BinaryOp::Division:
|
||||
op_string = "/";
|
||||
break;
|
||||
case BinaryOp::Modulo:
|
||||
op_string = "%";
|
||||
break;
|
||||
case BinaryOp::GreaterThan:
|
||||
op_string = ">";
|
||||
break;
|
||||
case BinaryOp::GreaterThanEquals:
|
||||
op_string = ">=";
|
||||
break;
|
||||
case BinaryOp::LessThan:
|
||||
op_string = "<";
|
||||
break;
|
||||
case BinaryOp::LessThanEquals:
|
||||
op_string = "<=";
|
||||
break;
|
||||
case BinaryOp::BitwiseAnd:
|
||||
op_string = "&";
|
||||
break;
|
||||
case BinaryOp::BitwiseOr:
|
||||
op_string = "|";
|
||||
break;
|
||||
case BinaryOp::BitwiseXor:
|
||||
op_string = "^";
|
||||
break;
|
||||
case BinaryOp::LeftShift:
|
||||
op_string = "<<";
|
||||
break;
|
||||
case BinaryOp::RightShift:
|
||||
op_string = ">>";
|
||||
break;
|
||||
case BinaryOp::EqualsEquals:
|
||||
op_string = "==";
|
||||
break;
|
||||
case BinaryOp::NotEqual:
|
||||
op_string = "!=";
|
||||
break;
|
||||
case BinaryOp::LogicalOr:
|
||||
op_string = "||";
|
||||
break;
|
||||
case BinaryOp::LogicalXor:
|
||||
op_string = "^^";
|
||||
break;
|
||||
case BinaryOp::LogicalAnd:
|
||||
op_string = "&&";
|
||||
break;
|
||||
case BinaryOp::Assignment:
|
||||
op_string = "=";
|
||||
break;
|
||||
case BinaryOp::AdditionAssignment:
|
||||
op_string = "+=";
|
||||
break;
|
||||
case BinaryOp::SubtractionAssignment:
|
||||
op_string = "-=";
|
||||
break;
|
||||
case BinaryOp::MultiplicationAssignment:
|
||||
op_string = "*=";
|
||||
break;
|
||||
case BinaryOp::DivisionAssignment:
|
||||
op_string = "/=";
|
||||
break;
|
||||
case BinaryOp::ModuloAssignment:
|
||||
op_string = "%=";
|
||||
break;
|
||||
case BinaryOp::AndAssignment:
|
||||
op_string = "&=";
|
||||
break;
|
||||
case BinaryOp::OrAssignment:
|
||||
op_string = "|=";
|
||||
break;
|
||||
case BinaryOp::XorAssignment:
|
||||
op_string = "^=";
|
||||
break;
|
||||
case BinaryOp::LeftShiftAssignment:
|
||||
op_string = "<<=";
|
||||
break;
|
||||
case BinaryOp::RightShiftAssignment:
|
||||
op_string = ">>=";
|
||||
break;
|
||||
}
|
||||
|
||||
TRY(m_lhs->dump(output, indent + 1));
|
||||
|
||||
TRY(print_indent(output, indent + 1));
|
||||
VERIFY(op_string);
|
||||
TRY(output.write_formatted("{}\n", op_string));
|
||||
|
||||
TRY(m_rhs->dump(output, indent + 1));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> FunctionCall::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(m_callee->dump(output, indent + 1));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("(\n"));
|
||||
for (auto const& arg : m_arguments) {
|
||||
TRY(arg->dump(output, indent + 1));
|
||||
}
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted(")\n"));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> StringLiteral::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("{}\n", m_value));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> ReturnStatement::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
if (m_value)
|
||||
TRY(m_value->dump(output, indent + 1));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> StructDeclaration::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("{}\n", m_name->name()));
|
||||
for (auto& member : m_members) {
|
||||
TRY(member->dump(output, indent + 1));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
Vector<NonnullRefPtr<Declaration const>> StructDeclaration::declarations() const
|
||||
{
|
||||
Vector<NonnullRefPtr<Declaration const>> declarations;
|
||||
for (auto& member : m_members)
|
||||
declarations.append(member);
|
||||
return declarations;
|
||||
}
|
||||
|
||||
ErrorOr<void> UnaryExpression::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
|
||||
char const* op_string = nullptr;
|
||||
switch (m_op) {
|
||||
case UnaryOp::BitwiseNot:
|
||||
op_string = "~";
|
||||
break;
|
||||
case UnaryOp::Not:
|
||||
op_string = "!";
|
||||
break;
|
||||
case UnaryOp::Plus:
|
||||
op_string = "+";
|
||||
break;
|
||||
case UnaryOp::Minus:
|
||||
op_string = "-";
|
||||
break;
|
||||
case UnaryOp::PlusPlus:
|
||||
op_string = "++";
|
||||
break;
|
||||
case UnaryOp::MinusMinus:
|
||||
op_string = "--";
|
||||
break;
|
||||
default:
|
||||
op_string = "<invalid>";
|
||||
}
|
||||
|
||||
VERIFY(op_string);
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("{} {}\n", m_is_postfix ? "postfix"sv : "prefix"sv, op_string));
|
||||
TRY(m_lhs->dump(output, indent + 1));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> BooleanLiteral::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("{}\n", m_value ? "true" : "false"));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> MemberExpression::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(m_object->dump(output, indent + 1));
|
||||
TRY(m_property->dump(output, indent + 1));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> ArrayElementExpression::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(m_array->dump(output, indent + 1));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("[\n"));
|
||||
TRY(m_index->dump(output, indent + 1));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("]\n"));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> BlockStatement::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
for (auto& statement : m_statements) {
|
||||
TRY(statement->dump(output, indent + 1));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> ForStatement::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
if (m_init) {
|
||||
TRY(print_indent(output, indent));
|
||||
TRY(output.write_formatted("Initializer:\n"));
|
||||
TRY(m_init->dump(output, indent + 1));
|
||||
}
|
||||
if (m_test) {
|
||||
TRY(print_indent(output, indent));
|
||||
TRY(output.write_formatted("Test expression:\n"));
|
||||
TRY(m_test->dump(output, indent + 1));
|
||||
}
|
||||
if (m_update) {
|
||||
TRY(print_indent(output, indent));
|
||||
TRY(output.write_formatted("Update expression:\n"));
|
||||
TRY(m_update->dump(output, indent + 1));
|
||||
}
|
||||
if (m_body) {
|
||||
TRY(print_indent(output, indent));
|
||||
TRY(output.write_formatted("Body:\n"));
|
||||
TRY(m_body->dump(output, indent + 1));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Vector<NonnullRefPtr<Declaration const>> Statement::declarations() const
|
||||
{
|
||||
if (is_declaration()) {
|
||||
Vector<NonnullRefPtr<Declaration const>> vec;
|
||||
auto const& decl = static_cast<Declaration const&>(*this);
|
||||
vec.empend(const_cast<Declaration&>(decl));
|
||||
return vec;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Vector<NonnullRefPtr<Declaration const>> ForStatement::declarations() const
|
||||
{
|
||||
Vector<NonnullRefPtr<Declaration const>> declarations;
|
||||
if (m_init)
|
||||
declarations.extend(m_init->declarations());
|
||||
if (m_body)
|
||||
declarations.extend(m_body->declarations());
|
||||
return declarations;
|
||||
}
|
||||
|
||||
Vector<NonnullRefPtr<Declaration const>> BlockStatement::declarations() const
|
||||
{
|
||||
Vector<NonnullRefPtr<Declaration const>> declarations;
|
||||
for (auto& statement : m_statements) {
|
||||
declarations.extend(statement->declarations());
|
||||
}
|
||||
return declarations;
|
||||
}
|
||||
|
||||
ErrorOr<void> IfStatement::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
if (m_predicate) {
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("Predicate:\n"));
|
||||
TRY(m_predicate->dump(output, indent + 1));
|
||||
}
|
||||
if (m_then) {
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("Then:\n"));
|
||||
TRY(m_then->dump(output, indent + 1));
|
||||
}
|
||||
if (m_else) {
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("Else:\n"));
|
||||
TRY(m_else->dump(output, indent + 1));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Vector<NonnullRefPtr<Declaration const>> IfStatement::declarations() const
|
||||
{
|
||||
Vector<NonnullRefPtr<Declaration const>> declarations;
|
||||
if (m_predicate)
|
||||
declarations.extend(m_predicate->declarations());
|
||||
if (m_then)
|
||||
declarations.extend(m_then->declarations());
|
||||
if (m_else)
|
||||
declarations.extend(m_else->declarations());
|
||||
return declarations;
|
||||
}
|
||||
|
||||
ErrorOr<void> Name::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(ASTNode::dump(output, indent));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
TRY(output.write_formatted("{}\n", name()));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> SizedName::dump(AK::Stream& output, size_t indent) const
|
||||
{
|
||||
TRY(Name::dump(output, indent));
|
||||
TRY(print_indent(output, indent + 1));
|
||||
|
||||
StringBuilder dimension_info;
|
||||
for (auto const& dim : m_dimensions) {
|
||||
dimension_info.append('[');
|
||||
dimension_info.append(dim);
|
||||
dimension_info.append(']');
|
||||
}
|
||||
|
||||
if (dimension_info.is_empty()) {
|
||||
dimension_info.append("[]"sv);
|
||||
}
|
||||
TRY(output.write_formatted("{}\n", dimension_info.string_view()));
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
715
Userland/Libraries/LibGLSL/AST.h
Normal file
715
Userland/Libraries/LibGLSL/AST.h
Normal file
|
@ -0,0 +1,715 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
* Copyright (c) 2023, Volodymyr V. <vvmposeydon@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibGLSL/Lexer.h>
|
||||
|
||||
namespace GLSL {
|
||||
|
||||
class ASTNode;
|
||||
class TranslationUnit;
|
||||
class Declaration;
|
||||
class FunctionDefinition;
|
||||
class Type;
|
||||
class Parameter;
|
||||
class Statement;
|
||||
class Name;
|
||||
|
||||
class ASTNode : public RefCounted<ASTNode> {
|
||||
public:
|
||||
virtual ~ASTNode() = default;
|
||||
virtual StringView class_name() const = 0;
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const;
|
||||
|
||||
template<typename T>
|
||||
bool fast_is() const = delete;
|
||||
|
||||
ASTNode const* parent() const { return m_parent; }
|
||||
Position start() const
|
||||
{
|
||||
VERIFY(m_start.has_value());
|
||||
return m_start.value();
|
||||
}
|
||||
Position end() const
|
||||
{
|
||||
VERIFY(m_end.has_value());
|
||||
return m_end.value();
|
||||
}
|
||||
FlyString const& filename() const
|
||||
{
|
||||
return m_filename;
|
||||
}
|
||||
void set_end(Position const& end) { m_end = end; }
|
||||
void set_parent(ASTNode const& parent) { m_parent = &parent; }
|
||||
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const { return {}; }
|
||||
|
||||
virtual bool is_variable_or_parameter_declaration() const { return false; }
|
||||
virtual bool is_function_call() const { return false; }
|
||||
virtual bool is_type() const { return false; }
|
||||
virtual bool is_declaration() const { return false; }
|
||||
virtual bool is_name() const { return false; }
|
||||
virtual bool is_member_expression() const { return true; }
|
||||
virtual bool is_dummy_node() const { return false; }
|
||||
|
||||
protected:
|
||||
ASTNode(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: m_parent(parent)
|
||||
, m_start(start)
|
||||
, m_end(end)
|
||||
, m_filename(filename)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
ASTNode const* m_parent { nullptr };
|
||||
Optional<Position> m_start;
|
||||
Optional<Position> m_end;
|
||||
FlyString m_filename;
|
||||
};
|
||||
|
||||
class TranslationUnit : public ASTNode {
|
||||
|
||||
public:
|
||||
virtual ~TranslationUnit() override = default;
|
||||
virtual StringView class_name() const override { return "TranslationUnit"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const override { return m_declarations; }
|
||||
|
||||
TranslationUnit(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: ASTNode(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
void set_declarations(Vector<NonnullRefPtr<Declaration const>>&& declarations) { m_declarations = move(declarations); }
|
||||
|
||||
private:
|
||||
Vector<NonnullRefPtr<Declaration const>> m_declarations;
|
||||
};
|
||||
|
||||
class Statement : public ASTNode {
|
||||
public:
|
||||
virtual ~Statement() override = default;
|
||||
virtual StringView class_name() const override { return "Statement"sv; }
|
||||
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const override;
|
||||
|
||||
protected:
|
||||
Statement(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: ASTNode(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Declaration : public Statement {
|
||||
|
||||
public:
|
||||
virtual bool is_declaration() const override { return true; }
|
||||
virtual bool is_variable_declaration() const { return false; }
|
||||
virtual bool is_parameter() const { return false; }
|
||||
virtual bool is_struct() const { return false; }
|
||||
virtual bool is_function() const { return false; }
|
||||
Name const* name() const { return m_name; }
|
||||
void set_name(RefPtr<Name const> name) { m_name = move(name); }
|
||||
|
||||
protected:
|
||||
Declaration(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<Name const> m_name;
|
||||
};
|
||||
|
||||
class InvalidDeclaration : public Declaration {
|
||||
|
||||
public:
|
||||
virtual ~InvalidDeclaration() override = default;
|
||||
virtual StringView class_name() const override { return "InvalidDeclaration"sv; }
|
||||
InvalidDeclaration(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Declaration(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class FunctionDeclaration : public Declaration {
|
||||
public:
|
||||
virtual ~FunctionDeclaration() override = default;
|
||||
virtual StringView class_name() const override { return "FunctionDeclaration"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
virtual bool is_function() const override { return true; }
|
||||
RefPtr<FunctionDefinition const> definition() { return m_definition; }
|
||||
|
||||
FunctionDeclaration(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Declaration(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const override;
|
||||
Type const* return_type() const { return m_return_type.ptr(); }
|
||||
void set_return_type(RefPtr<Type const> const& return_type) { m_return_type = return_type; }
|
||||
Vector<NonnullRefPtr<Parameter const>> const& parameters() const { return m_parameters; }
|
||||
void set_parameters(Vector<NonnullRefPtr<Parameter const>> const& parameters) { m_parameters = parameters; }
|
||||
FunctionDefinition const* definition() const { return m_definition.ptr(); }
|
||||
void set_definition(RefPtr<FunctionDefinition const>&& definition) { m_definition = move(definition); }
|
||||
|
||||
private:
|
||||
RefPtr<Type const> m_return_type;
|
||||
Vector<NonnullRefPtr<Parameter const>> m_parameters;
|
||||
RefPtr<FunctionDefinition const> m_definition;
|
||||
};
|
||||
|
||||
class VariableOrParameterDeclaration : public Declaration {
|
||||
public:
|
||||
virtual ~VariableOrParameterDeclaration() override = default;
|
||||
virtual bool is_variable_or_parameter_declaration() const override { return true; }
|
||||
|
||||
void set_type(RefPtr<Type const>&& type) { m_type = move(type); }
|
||||
Type const* type() const { return m_type.ptr(); }
|
||||
|
||||
protected:
|
||||
VariableOrParameterDeclaration(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Declaration(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<Type const> m_type;
|
||||
};
|
||||
|
||||
class Parameter : public VariableOrParameterDeclaration {
|
||||
public:
|
||||
virtual ~Parameter() override = default;
|
||||
virtual StringView class_name() const override { return "Parameter"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
virtual bool is_parameter() const override { return true; }
|
||||
|
||||
Parameter(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename, RefPtr<Name const> name)
|
||||
: VariableOrParameterDeclaration(parent, start, end, filename)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
};
|
||||
|
||||
enum class StorageTypeQualifier {
|
||||
Const,
|
||||
In,
|
||||
Out,
|
||||
Inout,
|
||||
Centroid,
|
||||
Patch,
|
||||
Sample,
|
||||
Uniform,
|
||||
Buffer,
|
||||
Shared,
|
||||
Coherent,
|
||||
Volatile,
|
||||
Restrict,
|
||||
Readonly,
|
||||
Writeonly,
|
||||
Subroutine,
|
||||
};
|
||||
|
||||
class Type : public ASTNode {
|
||||
public:
|
||||
virtual ~Type() override = default;
|
||||
virtual StringView class_name() const override { return "Type"sv; }
|
||||
virtual bool is_type() const override { return true; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
Type(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: ASTNode(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
Name const* name() const { return m_name.ptr(); }
|
||||
void set_name(RefPtr<Name const>&& name) { m_name = move(name); }
|
||||
|
||||
Vector<StorageTypeQualifier> const& storage_qualifiers() const { return m_storage_qualifiers; }
|
||||
void set_storage_qualifiers(Vector<StorageTypeQualifier>&& storage_qualifiers) { m_storage_qualifiers = move(storage_qualifiers); }
|
||||
|
||||
private:
|
||||
RefPtr<Name const> m_name;
|
||||
Vector<StorageTypeQualifier> m_storage_qualifiers;
|
||||
};
|
||||
|
||||
class FunctionDefinition : public ASTNode {
|
||||
public:
|
||||
virtual ~FunctionDefinition() override = default;
|
||||
virtual StringView class_name() const override { return "FunctionDefinition"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
FunctionDefinition(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: ASTNode(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const override;
|
||||
Vector<NonnullRefPtr<Statement const>> const& statements() { return m_statements; }
|
||||
void add_statement(NonnullRefPtr<Statement const>&& statement) { m_statements.append(move(statement)); }
|
||||
|
||||
private:
|
||||
Vector<NonnullRefPtr<Statement const>> m_statements;
|
||||
};
|
||||
|
||||
class InvalidStatement : public Statement {
|
||||
public:
|
||||
virtual ~InvalidStatement() override = default;
|
||||
virtual StringView class_name() const override { return "InvalidStatement"sv; }
|
||||
InvalidStatement(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Expression : public Statement {
|
||||
public:
|
||||
virtual ~Expression() override = default;
|
||||
virtual StringView class_name() const override { return "Expression"sv; }
|
||||
|
||||
protected:
|
||||
Expression(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class InvalidExpression : public Expression {
|
||||
public:
|
||||
virtual ~InvalidExpression() override = default;
|
||||
virtual StringView class_name() const override { return "InvalidExpression"sv; }
|
||||
InvalidExpression(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Expression(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class VariableDeclaration : public VariableOrParameterDeclaration {
|
||||
public:
|
||||
virtual ~VariableDeclaration() override = default;
|
||||
virtual StringView class_name() const override { return "VariableDeclaration"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
VariableDeclaration(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: VariableOrParameterDeclaration(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool is_variable_declaration() const override { return true; }
|
||||
|
||||
Expression const* initial_value() const { return m_initial_value; }
|
||||
void set_initial_value(RefPtr<Expression const>&& initial_value) { m_initial_value = move(initial_value); }
|
||||
|
||||
private:
|
||||
RefPtr<Expression const> m_initial_value;
|
||||
};
|
||||
|
||||
class Name : public Expression {
|
||||
public:
|
||||
virtual ~Name() override = default;
|
||||
virtual StringView class_name() const override { return "Name"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
virtual bool is_name() const override { return true; }
|
||||
virtual bool is_sized() const { return false; }
|
||||
|
||||
Name(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Expression(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
StringView name() const { return m_name; }
|
||||
void set_name(StringView name) { m_name = name; }
|
||||
|
||||
private:
|
||||
StringView m_name;
|
||||
};
|
||||
|
||||
class SizedName : public Name {
|
||||
public:
|
||||
virtual ~SizedName() override = default;
|
||||
virtual StringView class_name() const override { return "SizedName"sv; }
|
||||
virtual bool is_sized() const override { return true; }
|
||||
ErrorOr<void> dump(AK::Stream&, size_t indent) const override;
|
||||
|
||||
SizedName(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Name(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
void append_dimension(StringView dim) { m_dimensions.append(dim); }
|
||||
|
||||
private:
|
||||
Vector<StringView> m_dimensions;
|
||||
};
|
||||
|
||||
class NumericLiteral : public Expression {
|
||||
public:
|
||||
virtual ~NumericLiteral() override = default;
|
||||
virtual StringView class_name() const override { return "NumericLiteral"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
NumericLiteral(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename, StringView value)
|
||||
: Expression(parent, start, end, filename)
|
||||
, m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
StringView m_value;
|
||||
};
|
||||
|
||||
class BooleanLiteral : public Expression {
|
||||
public:
|
||||
virtual ~BooleanLiteral() override = default;
|
||||
virtual StringView class_name() const override { return "BooleanLiteral"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
BooleanLiteral(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename, bool value)
|
||||
: Expression(parent, start, end, filename)
|
||||
, m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_value;
|
||||
};
|
||||
|
||||
enum class BinaryOp {
|
||||
Addition,
|
||||
Subtraction,
|
||||
Multiplication,
|
||||
Division,
|
||||
Modulo,
|
||||
GreaterThan,
|
||||
GreaterThanEquals,
|
||||
LessThan,
|
||||
LessThanEquals,
|
||||
BitwiseAnd,
|
||||
BitwiseOr,
|
||||
BitwiseXor,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
EqualsEquals,
|
||||
NotEqual,
|
||||
LogicalOr,
|
||||
LogicalXor,
|
||||
LogicalAnd,
|
||||
Assignment,
|
||||
AdditionAssignment,
|
||||
SubtractionAssignment,
|
||||
MultiplicationAssignment,
|
||||
DivisionAssignment,
|
||||
ModuloAssignment,
|
||||
AndAssignment,
|
||||
OrAssignment,
|
||||
XorAssignment,
|
||||
LeftShiftAssignment,
|
||||
RightShiftAssignment,
|
||||
};
|
||||
|
||||
class BinaryExpression : public Expression {
|
||||
public:
|
||||
BinaryExpression(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Expression(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~BinaryExpression() override = default;
|
||||
virtual StringView class_name() const override { return "BinaryExpression"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
BinaryOp op() const { return m_op; }
|
||||
void set_op(BinaryOp op) { m_op = op; }
|
||||
Expression const* lhs() const { return m_lhs.ptr(); }
|
||||
void set_lhs(RefPtr<Expression const>&& e) { m_lhs = move(e); }
|
||||
Expression const* rhs() const { return m_rhs.ptr(); }
|
||||
void set_rhs(RefPtr<Expression const>&& e) { m_rhs = move(e); }
|
||||
|
||||
private:
|
||||
BinaryOp m_op;
|
||||
RefPtr<Expression const> m_lhs;
|
||||
RefPtr<Expression const> m_rhs;
|
||||
};
|
||||
|
||||
class FunctionCall : public Expression {
|
||||
public:
|
||||
FunctionCall(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Expression(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~FunctionCall() override = default;
|
||||
virtual StringView class_name() const override { return "FunctionCall"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
virtual bool is_function_call() const override { return true; }
|
||||
|
||||
Expression const* callee() const { return m_callee; }
|
||||
void set_callee(RefPtr<Expression const>&& callee) { m_callee = move(callee); }
|
||||
|
||||
void set_arguments(Vector<NonnullRefPtr<Expression const>>&& args) { m_arguments = move(args); }
|
||||
Vector<NonnullRefPtr<Expression const>> const& arguments() const { return m_arguments; }
|
||||
|
||||
private:
|
||||
RefPtr<Expression const> m_callee;
|
||||
Vector<NonnullRefPtr<Expression const>> m_arguments;
|
||||
};
|
||||
|
||||
class StringLiteral final : public Expression {
|
||||
public:
|
||||
StringLiteral(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Expression(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
~StringLiteral() override = default;
|
||||
virtual StringView class_name() const override { return "StringLiteral"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
String const& value() const { return m_value; }
|
||||
void set_value(String value) { m_value = move(value); }
|
||||
|
||||
private:
|
||||
String m_value;
|
||||
};
|
||||
|
||||
class ReturnStatement : public Statement {
|
||||
public:
|
||||
virtual ~ReturnStatement() override = default;
|
||||
virtual StringView class_name() const override { return "ReturnStatement"sv; }
|
||||
|
||||
ReturnStatement(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
Expression const* value() const { return m_value.ptr(); }
|
||||
void set_value(RefPtr<Expression const>&& value) { m_value = move(value); }
|
||||
|
||||
private:
|
||||
RefPtr<Expression const> m_value;
|
||||
};
|
||||
|
||||
class DiscardStatement : public Statement {
|
||||
public:
|
||||
virtual ~DiscardStatement() override = default;
|
||||
virtual StringView class_name() const override { return "DiscardStatement"sv; }
|
||||
|
||||
DiscardStatement(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class StructDeclaration : public Declaration {
|
||||
public:
|
||||
virtual ~StructDeclaration() override = default;
|
||||
virtual StringView class_name() const override { return "StructDeclaration"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
virtual bool is_struct() const override { return true; }
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const override;
|
||||
|
||||
StructDeclaration(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Declaration(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
Vector<NonnullRefPtr<Declaration const>> const& members() const { return m_members; }
|
||||
void set_members(Vector<NonnullRefPtr<Declaration const>>&& members) { m_members = move(members); }
|
||||
|
||||
private:
|
||||
Vector<NonnullRefPtr<Declaration const>> m_members;
|
||||
};
|
||||
|
||||
enum class UnaryOp {
|
||||
BitwiseNot,
|
||||
Not,
|
||||
Plus,
|
||||
Minus,
|
||||
PlusPlus,
|
||||
MinusMinus,
|
||||
};
|
||||
|
||||
class UnaryExpression : public Expression {
|
||||
public:
|
||||
UnaryExpression(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Expression(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~UnaryExpression() override = default;
|
||||
virtual StringView class_name() const override { return "UnaryExpression"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
UnaryOp op() const { return m_op; }
|
||||
void set_op(UnaryOp op) { m_op = op; }
|
||||
Expression const* lhs() const { return m_lhs.ptr(); }
|
||||
void set_lhs(RefPtr<Expression const>&& e) { m_lhs = move(e); }
|
||||
bool is_postfix() const { return m_is_postfix; }
|
||||
void set_is_postfix(bool is_postfix) { m_is_postfix = is_postfix; }
|
||||
|
||||
private:
|
||||
UnaryOp m_op;
|
||||
RefPtr<Expression const> m_lhs;
|
||||
bool m_is_postfix = false;
|
||||
};
|
||||
|
||||
class MemberExpression : public Expression {
|
||||
public:
|
||||
MemberExpression(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Expression(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MemberExpression() override = default;
|
||||
virtual StringView class_name() const override { return "MemberExpression"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
virtual bool is_member_expression() const override { return true; }
|
||||
|
||||
Expression const* object() const { return m_object.ptr(); }
|
||||
void set_object(RefPtr<Expression const>&& object) { m_object = move(object); }
|
||||
Expression const* property() const { return m_property.ptr(); }
|
||||
void set_property(RefPtr<Expression const>&& property) { m_property = move(property); }
|
||||
|
||||
private:
|
||||
RefPtr<Expression const> m_object;
|
||||
RefPtr<Expression const> m_property;
|
||||
};
|
||||
|
||||
class ArrayElementExpression : public Expression {
|
||||
public:
|
||||
ArrayElementExpression(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Expression(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ArrayElementExpression() override = default;
|
||||
virtual StringView class_name() const override { return "ArrayElementExpression"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
Expression const* array() const { return m_array.ptr(); }
|
||||
void set_array(RefPtr<Expression const>&& array) { m_array = move(array); }
|
||||
Expression const* index() const { return m_index.ptr(); }
|
||||
void set_index(RefPtr<Expression const>&& index) { m_index = move(index); }
|
||||
|
||||
private:
|
||||
RefPtr<Expression const> m_array;
|
||||
RefPtr<Expression const> m_index;
|
||||
};
|
||||
|
||||
class ForStatement : public Statement {
|
||||
public:
|
||||
ForStatement(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ForStatement() override = default;
|
||||
virtual StringView class_name() const override { return "ForStatement"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const override;
|
||||
|
||||
void set_init(RefPtr<VariableDeclaration const>&& init) { m_init = move(init); }
|
||||
void set_test(RefPtr<Expression const>&& test) { m_test = move(test); }
|
||||
void set_update(RefPtr<Expression const>&& update) { m_update = move(update); }
|
||||
void set_body(RefPtr<Statement const>&& body) { m_body = move(body); }
|
||||
Statement const* body() const { return m_body.ptr(); }
|
||||
|
||||
private:
|
||||
RefPtr<VariableDeclaration const> m_init;
|
||||
RefPtr<Expression const> m_test;
|
||||
RefPtr<Expression const> m_update;
|
||||
RefPtr<Statement const> m_body;
|
||||
};
|
||||
|
||||
class BlockStatement final : public Statement {
|
||||
public:
|
||||
BlockStatement(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~BlockStatement() override = default;
|
||||
virtual StringView class_name() const override { return "BlockStatement"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const override;
|
||||
|
||||
void add_statement(NonnullRefPtr<Statement const>&& statement) { m_statements.append(move(statement)); }
|
||||
|
||||
private:
|
||||
Vector<NonnullRefPtr<Statement const>> m_statements;
|
||||
};
|
||||
|
||||
class IfStatement : public Statement {
|
||||
public:
|
||||
IfStatement(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: Statement(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~IfStatement() override = default;
|
||||
virtual StringView class_name() const override { return "IfStatement"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t indent = 0) const override;
|
||||
virtual Vector<NonnullRefPtr<Declaration const>> declarations() const override;
|
||||
|
||||
void set_predicate(RefPtr<Expression const>&& predicate) { m_predicate = move(predicate); }
|
||||
void set_then_statement(RefPtr<Statement const>&& then) { m_then = move(then); }
|
||||
void set_else_statement(RefPtr<Statement const>&& _else) { m_else = move(_else); }
|
||||
|
||||
Statement const* then_statement() const { return m_then.ptr(); }
|
||||
Statement const* else_statement() const { return m_else.ptr(); }
|
||||
|
||||
private:
|
||||
RefPtr<Expression const> m_predicate;
|
||||
RefPtr<Statement const> m_then;
|
||||
RefPtr<Statement const> m_else;
|
||||
};
|
||||
|
||||
class DummyAstNode : public ASTNode {
|
||||
public:
|
||||
DummyAstNode(ASTNode const* parent, Optional<Position> start, Optional<Position> end, String const& filename)
|
||||
: ASTNode(parent, start, end, filename)
|
||||
{
|
||||
}
|
||||
virtual bool is_dummy_node() const override { return true; }
|
||||
virtual StringView class_name() const override { return "DummyAstNode"sv; }
|
||||
virtual ErrorOr<void> dump(AK::Stream&, size_t = 0) const override { return {}; }
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<MemberExpression>() const { return is_member_expression(); }
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<VariableOrParameterDeclaration>() const { return is_variable_or_parameter_declaration(); }
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<FunctionCall>() const { return is_function_call(); }
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<Type>() const { return is_type(); }
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<Declaration>() const { return is_declaration(); }
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<Name>() const { return is_name(); }
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<DummyAstNode>() const { return is_dummy_node(); }
|
||||
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<VariableDeclaration>() const { return is_declaration() && verify_cast<Declaration>(*this).is_variable_declaration(); }
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<FunctionDeclaration>() const { return is_declaration() && verify_cast<Declaration>(*this).is_function(); }
|
||||
|
||||
template<>
|
||||
inline bool ASTNode::fast_is<SizedName>() const { return is_name() && verify_cast<Name>(*this).is_sized(); }
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
set(SOURCES
|
||||
AST.cpp
|
||||
Compiler.cpp
|
||||
Lexer.cpp
|
||||
Linker.cpp
|
||||
Parser.cpp
|
||||
Preprocessor.cpp
|
||||
Token.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibGLSL glsl)
|
||||
|
|
819
Userland/Libraries/LibGLSL/Lexer.cpp
Normal file
819
Userland/Libraries/LibGLSL/Lexer.cpp
Normal file
|
@ -0,0 +1,819 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2023, Volodymyr V. <vvmposeydon@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Lexer.h"
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/String.h>
|
||||
|
||||
namespace GLSL {
|
||||
|
||||
Lexer::Lexer(StringView input, size_t start_line)
|
||||
: m_input(input)
|
||||
, m_previous_position { start_line, 0 }
|
||||
, m_position { start_line, 0 }
|
||||
{
|
||||
}
|
||||
|
||||
char Lexer::peek(size_t offset) const
|
||||
{
|
||||
if ((m_index + offset) >= m_input.length())
|
||||
return 0;
|
||||
return m_input[m_index + offset];
|
||||
}
|
||||
|
||||
char Lexer::consume()
|
||||
{
|
||||
VERIFY(m_index < m_input.length());
|
||||
char ch = m_input[m_index++];
|
||||
m_previous_position = m_position;
|
||||
if (ch == '\n') {
|
||||
m_position.line++;
|
||||
m_position.column = 0;
|
||||
} else {
|
||||
m_position.column++;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
constexpr bool is_valid_first_character_of_identifier(char ch)
|
||||
{
|
||||
return is_ascii_alpha(ch) || ch == '_' || ch == '$';
|
||||
}
|
||||
|
||||
constexpr bool is_valid_nonfirst_character_of_identifier(char ch)
|
||||
{
|
||||
return is_valid_first_character_of_identifier(ch) || is_ascii_digit(ch);
|
||||
}
|
||||
|
||||
// NOTE: some of these keywords are not used at the moment, however they are reserved for future use and should not be used as identifiers
|
||||
constexpr Array<StringView, 66> s_known_keywords = {
|
||||
"asm"sv,
|
||||
"attribute"sv,
|
||||
"break"sv,
|
||||
"case"sv,
|
||||
"cast"sv,
|
||||
"centroid"sv,
|
||||
"class"sv,
|
||||
"common"
|
||||
"partition"sv,
|
||||
"active"sv,
|
||||
"const"sv,
|
||||
"continue"sv,
|
||||
"default"sv,
|
||||
"discard"sv,
|
||||
"do"sv,
|
||||
"else"sv,
|
||||
"enum"sv,
|
||||
"extern"sv,
|
||||
"external"sv,
|
||||
"false"sv,
|
||||
"filter"sv,
|
||||
"fixed"sv,
|
||||
"flat"sv,
|
||||
"for"sv,
|
||||
"goto"sv,
|
||||
"half"sv,
|
||||
"highp"sv,
|
||||
"if"sv,
|
||||
"in"sv,
|
||||
"inline"sv,
|
||||
"inout"sv,
|
||||
"input"sv,
|
||||
"interface"sv,
|
||||
"invariant"sv,
|
||||
"layout"sv,
|
||||
"lowp"sv,
|
||||
"mediump"sv,
|
||||
"namespace"sv,
|
||||
"noinline"sv,
|
||||
"noperspective"sv,
|
||||
"out"sv,
|
||||
"output"sv,
|
||||
"packed"sv,
|
||||
"patch"sv,
|
||||
"precision"sv,
|
||||
"public"sv,
|
||||
"return"sv,
|
||||
"row_major"sv,
|
||||
"sample"sv,
|
||||
"sizeof"sv,
|
||||
"smooth"sv,
|
||||
"static"sv,
|
||||
"struct"sv,
|
||||
"subroutine"sv,
|
||||
"superp"sv,
|
||||
"switch"sv,
|
||||
"template"sv,
|
||||
"this"sv,
|
||||
"true"sv,
|
||||
"typedef"sv,
|
||||
"uniform"sv,
|
||||
"union"sv,
|
||||
"using"sv,
|
||||
"varying"sv,
|
||||
"volatile"sv,
|
||||
"while"sv,
|
||||
};
|
||||
|
||||
constexpr Array<StringView, 120> s_known_types = {
|
||||
"bool"sv,
|
||||
"bvec2"sv,
|
||||
"bvec3"sv,
|
||||
"bvec4"sv,
|
||||
"dmat2"sv,
|
||||
"dmat2x2"sv,
|
||||
"dmat2x3"sv,
|
||||
"dmat2x4"sv,
|
||||
"dmat3"sv,
|
||||
"dmat3x2"sv,
|
||||
"dmat3x3"sv,
|
||||
"dmat3x4"sv,
|
||||
"dmat4"sv,
|
||||
"dmat4x2"sv,
|
||||
"dmat4x3"sv,
|
||||
"dmat4x4"sv,
|
||||
"double"sv,
|
||||
"dvec2"sv,
|
||||
"dvec3"sv,
|
||||
"dvec4"sv,
|
||||
"float"sv,
|
||||
"fvec2"sv,
|
||||
"fvec3"sv,
|
||||
"fvec4"sv,
|
||||
"hvec2"sv,
|
||||
"hvec3"sv,
|
||||
"hvec4"sv,
|
||||
"iimage1D"sv,
|
||||
"iimage1DArray"sv,
|
||||
"iimage2D"sv,
|
||||
"iimage2DArray"sv,
|
||||
"iimage3D"sv,
|
||||
"iimageBuffer"sv,
|
||||
"iimageCube"sv,
|
||||
"image1D"sv,
|
||||
"image1DArray"sv,
|
||||
"image1DArrayShadow"sv,
|
||||
"image1DShadow"sv,
|
||||
"image2D"sv,
|
||||
"image2DArray"sv,
|
||||
"image2DArrayShadow"sv,
|
||||
"image2DShadow"sv,
|
||||
"image3D"sv,
|
||||
"imageBuffer"sv,
|
||||
"imageCube"sv,
|
||||
"int"sv,
|
||||
"isampler1D"sv,
|
||||
"isampler1DArray"sv,
|
||||
"isampler2D"sv,
|
||||
"isampler2DArray"sv,
|
||||
"isampler2DMS"sv,
|
||||
"isampler2DMSArray"sv,
|
||||
"isampler2DRect"sv,
|
||||
"isampler3D"sv,
|
||||
"isamplerBuffer"sv,
|
||||
"isamplerCube"sv,
|
||||
"isamplerCubeArray"sv,
|
||||
"ivec2"sv,
|
||||
"ivec3"sv,
|
||||
"ivec4"sv,
|
||||
"long"sv,
|
||||
"mat2"sv,
|
||||
"mat2x2"sv,
|
||||
"mat2x3"sv,
|
||||
"mat2x4"sv,
|
||||
"mat3"sv,
|
||||
"mat3x2"sv,
|
||||
"mat3x3"sv,
|
||||
"mat3x4"sv,
|
||||
"mat4"sv,
|
||||
"mat4x2"sv,
|
||||
"mat4x3"sv,
|
||||
"mat4x4"sv,
|
||||
"sampler1D"sv,
|
||||
"sampler1DArray"sv,
|
||||
"sampler1DArrayShadow"sv,
|
||||
"sampler1DShadow"sv,
|
||||
"sampler2D"sv,
|
||||
"sampler2DArray"sv,
|
||||
"sampler2DArrayShadow"sv,
|
||||
"sampler2DMS"sv,
|
||||
"sampler2DMSArray"sv,
|
||||
"sampler2DRect"sv,
|
||||
"sampler2DRectShadow"sv,
|
||||
"sampler2DShadow"sv,
|
||||
"sampler3D"sv,
|
||||
"sampler3DRect"sv,
|
||||
"samplerBuffer"sv,
|
||||
"samplerCube"sv,
|
||||
"samplerCubeArray"sv,
|
||||
"samplerCubeArrayShadow"sv,
|
||||
"samplerCubeShadow"sv,
|
||||
"short"sv,
|
||||
"uimage1D"sv,
|
||||
"uimage1DArray"sv,
|
||||
"uimage2D"sv,
|
||||
"uimage2DArray"sv,
|
||||
"uimage3D"sv,
|
||||
"uimageBuffer"sv,
|
||||
"uimageCube"sv,
|
||||
"uint"sv,
|
||||
"unsigned"sv,
|
||||
"usampler1D"sv,
|
||||
"usampler1DArray"sv,
|
||||
"usampler2D"sv,
|
||||
"usampler2DArray"sv,
|
||||
"usampler2DMS"sv,
|
||||
"usampler2DMSArray"sv,
|
||||
"usampler2DRect"sv,
|
||||
"usampler3D"sv,
|
||||
"usamplerBuffer"sv,
|
||||
"usamplerCube"sv,
|
||||
"usamplerCubeArray"sv,
|
||||
"uvec2"sv,
|
||||
"uvec3"sv,
|
||||
"uvec4"sv,
|
||||
"vec2"sv,
|
||||
"vec3"sv,
|
||||
"vec4"sv,
|
||||
"void"sv,
|
||||
};
|
||||
|
||||
static bool is_keyword(StringView string)
|
||||
{
|
||||
return AK::find(s_known_keywords.begin(), s_known_keywords.end(), string) != s_known_keywords.end();
|
||||
}
|
||||
|
||||
static bool is_known_type(StringView string)
|
||||
{
|
||||
return AK::find(s_known_types.begin(), s_known_types.end(), string) != s_known_types.end();
|
||||
}
|
||||
|
||||
void Lexer::lex_impl(Function<void(Token)> callback)
|
||||
{
|
||||
size_t token_start_index = 0;
|
||||
Position token_start_position;
|
||||
|
||||
auto emit_single_char_token = [&](auto type) {
|
||||
callback(Token(type, m_position, m_position, m_input.substring_view(m_index, 1)));
|
||||
consume();
|
||||
};
|
||||
|
||||
auto begin_token = [&] {
|
||||
token_start_index = m_index;
|
||||
token_start_position = m_position;
|
||||
};
|
||||
auto commit_token = [&](auto type) {
|
||||
if (m_options.ignore_whitespace && type == Token::Type::Whitespace)
|
||||
return;
|
||||
callback(Token(type, token_start_position, m_previous_position, m_input.substring_view(token_start_index, m_index - token_start_index)));
|
||||
};
|
||||
|
||||
auto emit_token_equals = [&](auto type, auto equals_type) {
|
||||
if (peek(1) == '=') {
|
||||
begin_token();
|
||||
consume();
|
||||
consume();
|
||||
commit_token(equals_type);
|
||||
return;
|
||||
}
|
||||
emit_single_char_token(type);
|
||||
};
|
||||
|
||||
auto match_escape_sequence = [&]() -> size_t {
|
||||
switch (peek(1)) {
|
||||
case '\'':
|
||||
case '"':
|
||||
case '?':
|
||||
case '\\':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'f':
|
||||
case 'n':
|
||||
case 'r':
|
||||
case 't':
|
||||
case 'v':
|
||||
return 2;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7': {
|
||||
size_t octal_digits = 1;
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
char next = peek(2 + i);
|
||||
if (next < '0' || next > '7')
|
||||
break;
|
||||
++octal_digits;
|
||||
}
|
||||
return 1 + octal_digits;
|
||||
}
|
||||
case 'x': {
|
||||
size_t hex_digits = 0;
|
||||
while (is_ascii_hex_digit(peek(2 + hex_digits)))
|
||||
++hex_digits;
|
||||
return 2 + hex_digits;
|
||||
}
|
||||
case 'u':
|
||||
case 'U': {
|
||||
bool is_unicode = true;
|
||||
size_t number_of_digits = peek(1) == 'u' ? 4 : 8;
|
||||
for (size_t i = 0; i < number_of_digits; ++i) {
|
||||
if (!is_ascii_hex_digit(peek(2 + i))) {
|
||||
is_unicode = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return is_unicode ? 2 + number_of_digits : 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
auto match_string_prefix = [&](char quote) -> size_t {
|
||||
if (peek() == quote)
|
||||
return 1;
|
||||
if (peek() == 'L' && peek(1) == quote)
|
||||
return 2;
|
||||
if (peek() == 'u') {
|
||||
if (peek(1) == quote)
|
||||
return 2;
|
||||
if (peek(1) == '8' && peek(2) == quote)
|
||||
return 3;
|
||||
}
|
||||
if (peek() == 'U' && peek(1) == quote)
|
||||
return 2;
|
||||
return 0;
|
||||
};
|
||||
|
||||
while (m_index < m_input.length()) {
|
||||
auto ch = peek();
|
||||
if (is_ascii_space(ch)) {
|
||||
begin_token();
|
||||
while (is_ascii_space(peek()))
|
||||
consume();
|
||||
commit_token(Token::Type::Whitespace);
|
||||
continue;
|
||||
}
|
||||
if (ch == '(') {
|
||||
emit_single_char_token(Token::Type::LeftParen);
|
||||
continue;
|
||||
}
|
||||
if (ch == ')') {
|
||||
emit_single_char_token(Token::Type::RightParen);
|
||||
continue;
|
||||
}
|
||||
if (ch == '{') {
|
||||
emit_single_char_token(Token::Type::LeftCurly);
|
||||
continue;
|
||||
}
|
||||
if (ch == '}') {
|
||||
emit_single_char_token(Token::Type::RightCurly);
|
||||
continue;
|
||||
}
|
||||
if (ch == '[') {
|
||||
emit_single_char_token(Token::Type::LeftBracket);
|
||||
continue;
|
||||
}
|
||||
if (ch == ']') {
|
||||
emit_single_char_token(Token::Type::RightBracket);
|
||||
continue;
|
||||
}
|
||||
if (ch == '<') {
|
||||
begin_token();
|
||||
consume();
|
||||
if (peek() == '<') {
|
||||
consume();
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::LessLessEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::LessLess);
|
||||
continue;
|
||||
}
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::LessEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::Less);
|
||||
continue;
|
||||
}
|
||||
if (ch == '>') {
|
||||
begin_token();
|
||||
consume();
|
||||
if (peek() == '>') {
|
||||
consume();
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::GreaterGreaterEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::GreaterGreater);
|
||||
continue;
|
||||
}
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::GreaterEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::Greater);
|
||||
continue;
|
||||
}
|
||||
if (ch == ',') {
|
||||
emit_single_char_token(Token::Type::Comma);
|
||||
continue;
|
||||
}
|
||||
if (ch == '+') {
|
||||
begin_token();
|
||||
consume();
|
||||
if (peek() == '+') {
|
||||
consume();
|
||||
commit_token(Token::Type::PlusPlus);
|
||||
continue;
|
||||
}
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::PlusEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::Plus);
|
||||
continue;
|
||||
}
|
||||
if (ch == '-') {
|
||||
begin_token();
|
||||
consume();
|
||||
if (peek() == '-') {
|
||||
consume();
|
||||
commit_token(Token::Type::MinusMinus);
|
||||
continue;
|
||||
}
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::MinusEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::Minus);
|
||||
continue;
|
||||
}
|
||||
if (ch == '*') {
|
||||
emit_token_equals(Token::Type::Asterisk, Token::Type::AsteriskEquals);
|
||||
continue;
|
||||
}
|
||||
if (ch == '%') {
|
||||
emit_token_equals(Token::Type::Percent, Token::Type::PercentEquals);
|
||||
continue;
|
||||
}
|
||||
if (ch == '^') {
|
||||
begin_token();
|
||||
consume();
|
||||
if (peek() == '^') {
|
||||
consume();
|
||||
commit_token(Token::Type::CaretCaret);
|
||||
continue;
|
||||
}
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::CaretEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::Caret);
|
||||
continue;
|
||||
}
|
||||
if (ch == '!') {
|
||||
emit_token_equals(Token::Type::ExclamationMark, Token::Type::ExclamationMarkEquals);
|
||||
continue;
|
||||
}
|
||||
if (ch == '=') {
|
||||
emit_token_equals(Token::Type::Equals, Token::Type::EqualsEquals);
|
||||
continue;
|
||||
}
|
||||
if (ch == '&') {
|
||||
begin_token();
|
||||
consume();
|
||||
if (peek() == '&') {
|
||||
consume();
|
||||
commit_token(Token::Type::AndAnd);
|
||||
continue;
|
||||
}
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::AndEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::And);
|
||||
continue;
|
||||
}
|
||||
if (ch == '|') {
|
||||
begin_token();
|
||||
consume();
|
||||
if (peek() == '|') {
|
||||
consume();
|
||||
commit_token(Token::Type::PipePipe);
|
||||
continue;
|
||||
}
|
||||
if (peek() == '=') {
|
||||
consume();
|
||||
commit_token(Token::Type::PipeEquals);
|
||||
continue;
|
||||
}
|
||||
commit_token(Token::Type::Pipe);
|
||||
continue;
|
||||
}
|
||||
if (ch == '~') {
|
||||
emit_single_char_token(Token::Type::Tilde);
|
||||
continue;
|
||||
}
|
||||
if (ch == '?') {
|
||||
emit_single_char_token(Token::Type::QuestionMark);
|
||||
continue;
|
||||
}
|
||||
if (ch == ':') {
|
||||
emit_single_char_token(Token::Type::Colon);
|
||||
continue;
|
||||
}
|
||||
if (ch == ';') {
|
||||
emit_single_char_token(Token::Type::Semicolon);
|
||||
continue;
|
||||
}
|
||||
if (ch == '.') {
|
||||
emit_single_char_token(Token::Type::Dot);
|
||||
continue;
|
||||
}
|
||||
if (ch == '#') {
|
||||
begin_token();
|
||||
consume();
|
||||
while (AK::is_ascii_space(peek()))
|
||||
consume();
|
||||
|
||||
size_t directive_start = m_index;
|
||||
if (is_valid_first_character_of_identifier(peek()))
|
||||
while (peek() && is_valid_nonfirst_character_of_identifier(peek()))
|
||||
consume();
|
||||
|
||||
auto directive = StringView(m_input.characters_without_null_termination() + directive_start, m_index - directive_start);
|
||||
if (directive == "include"sv) {
|
||||
commit_token(Token::Type::IncludeStatement);
|
||||
|
||||
if (is_ascii_space(peek())) {
|
||||
begin_token();
|
||||
do {
|
||||
consume();
|
||||
} while (is_ascii_space(peek()));
|
||||
commit_token(Token::Type::Whitespace);
|
||||
}
|
||||
|
||||
begin_token();
|
||||
if (peek() == '<' || peek() == '"') {
|
||||
char closing = consume() == '<' ? '>' : '"';
|
||||
while (peek() && peek() != closing && peek() != '\n')
|
||||
consume();
|
||||
|
||||
if (peek() && consume() == '\n') {
|
||||
commit_token(Token::Type::IncludePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
commit_token(Token::Type::IncludePath);
|
||||
begin_token();
|
||||
}
|
||||
} else {
|
||||
while (peek()) {
|
||||
if (peek() == '\\' && peek(1) == '\n') {
|
||||
consume();
|
||||
consume();
|
||||
} else if (peek() == '\n') {
|
||||
break;
|
||||
} else {
|
||||
consume();
|
||||
}
|
||||
}
|
||||
|
||||
commit_token(Token::Type::PreprocessorStatement);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
if (ch == '/' && peek(1) == '/') {
|
||||
while (peek() && peek() != '\n')
|
||||
consume();
|
||||
continue;
|
||||
}
|
||||
if (ch == '/' && peek(1) == '*') {
|
||||
consume();
|
||||
consume();
|
||||
bool comment_block_ends = false;
|
||||
while (peek()) {
|
||||
if (peek() == '*' && peek(1) == '/') {
|
||||
comment_block_ends = true;
|
||||
break;
|
||||
}
|
||||
|
||||
consume();
|
||||
}
|
||||
|
||||
if (comment_block_ends) {
|
||||
consume();
|
||||
consume();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ch == '/') {
|
||||
emit_token_equals(Token::Type::Slash, Token::Type::SlashEquals);
|
||||
continue;
|
||||
}
|
||||
if (size_t prefix = match_string_prefix('"'); prefix > 0) {
|
||||
begin_token();
|
||||
for (size_t i = 0; i < prefix; ++i)
|
||||
consume();
|
||||
while (peek()) {
|
||||
if (peek() == '\\') {
|
||||
if (size_t escape = match_escape_sequence(); escape > 0) {
|
||||
commit_token(Token::Type::DoubleQuotedString);
|
||||
begin_token();
|
||||
for (size_t i = 0; i < escape; ++i)
|
||||
consume();
|
||||
commit_token(Token::Type::EscapeSequence);
|
||||
begin_token();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If string is not terminated - stop before EOF
|
||||
if (!peek(1))
|
||||
break;
|
||||
|
||||
if (consume() == '"')
|
||||
break;
|
||||
}
|
||||
commit_token(Token::Type::DoubleQuotedString);
|
||||
continue;
|
||||
}
|
||||
if (size_t prefix = match_string_prefix('R'); prefix > 0 && peek(prefix) == '"') {
|
||||
begin_token();
|
||||
for (size_t i = 0; i < prefix + 1; ++i)
|
||||
consume();
|
||||
size_t prefix_start = m_index;
|
||||
while (peek() && peek() != '(')
|
||||
consume();
|
||||
StringView prefix_string = m_input.substring_view(prefix_start, m_index - prefix_start);
|
||||
while (peek()) {
|
||||
if (consume() == '"') {
|
||||
VERIFY(m_index >= prefix_string.length() + 2);
|
||||
VERIFY(m_input[m_index - 1] == '"');
|
||||
if (m_input[m_index - 1 - prefix_string.length() - 1] == ')') {
|
||||
StringView suffix_string = m_input.substring_view(m_index - 1 - prefix_string.length(), prefix_string.length());
|
||||
if (prefix_string == suffix_string)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
commit_token(Token::Type::RawString);
|
||||
continue;
|
||||
}
|
||||
if (size_t prefix = match_string_prefix('\''); prefix > 0) {
|
||||
begin_token();
|
||||
for (size_t i = 0; i < prefix; ++i)
|
||||
consume();
|
||||
while (peek()) {
|
||||
if (peek() == '\\') {
|
||||
if (size_t escape = match_escape_sequence(); escape > 0) {
|
||||
commit_token(Token::Type::SingleQuotedString);
|
||||
begin_token();
|
||||
for (size_t i = 0; i < escape; ++i)
|
||||
consume();
|
||||
commit_token(Token::Type::EscapeSequence);
|
||||
begin_token();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (consume() == '\'')
|
||||
break;
|
||||
}
|
||||
commit_token(Token::Type::SingleQuotedString);
|
||||
continue;
|
||||
}
|
||||
if (is_ascii_digit(ch) || (ch == '.' && is_ascii_digit(peek(1)))) {
|
||||
begin_token();
|
||||
consume();
|
||||
|
||||
auto type = ch == '.' ? Token::Type::Float : Token::Type::Integer;
|
||||
bool is_hex = false;
|
||||
bool is_binary = false;
|
||||
|
||||
auto match_exponent = [&]() -> size_t {
|
||||
char ch = peek();
|
||||
if (ch != 'e' && ch != 'E' && ch != 'p' && ch != 'P')
|
||||
return 0;
|
||||
|
||||
type = Token::Type::Float;
|
||||
size_t length = 1;
|
||||
ch = peek(length);
|
||||
if (ch == '+' || ch == '-') {
|
||||
++length;
|
||||
}
|
||||
for (ch = peek(length); is_ascii_digit(ch); ch = peek(length)) {
|
||||
++length;
|
||||
}
|
||||
return length;
|
||||
};
|
||||
|
||||
auto match_type_literal = [&]() -> size_t {
|
||||
size_t length = 0;
|
||||
for (;;) {
|
||||
char ch = peek(length);
|
||||
if ((ch == 'u' || ch == 'U') && type == Token::Type::Integer) {
|
||||
++length;
|
||||
} else if ((ch == 'f' || ch == 'F') && !is_binary) {
|
||||
type = Token::Type::Float;
|
||||
++length;
|
||||
} else if (ch == 'l' || ch == 'L') {
|
||||
++length;
|
||||
} else
|
||||
return length;
|
||||
}
|
||||
};
|
||||
|
||||
if (peek() == 'b' || peek() == 'B') {
|
||||
consume();
|
||||
is_binary = true;
|
||||
for (char ch = peek(); ch == '0' || ch == '1' || (ch == '\'' && peek(1) != '\''); ch = peek()) {
|
||||
consume();
|
||||
}
|
||||
} else {
|
||||
if (peek() == 'x' || peek() == 'X') {
|
||||
consume();
|
||||
is_hex = true;
|
||||
}
|
||||
|
||||
for (char ch = peek(); (is_hex ? is_ascii_hex_digit(ch) : is_ascii_digit(ch)) || (ch == '\'' && peek(1) != '\'') || ch == '.'; ch = peek()) {
|
||||
if (ch == '.') {
|
||||
if (type == Token::Type::Integer) {
|
||||
type = Token::Type::Float;
|
||||
} else
|
||||
break;
|
||||
};
|
||||
consume();
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_binary) {
|
||||
size_t length = match_exponent();
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
consume();
|
||||
}
|
||||
|
||||
size_t length = match_type_literal();
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
consume();
|
||||
|
||||
commit_token(type);
|
||||
continue;
|
||||
}
|
||||
if (is_valid_first_character_of_identifier(ch)) {
|
||||
begin_token();
|
||||
while (peek() && is_valid_nonfirst_character_of_identifier(peek()))
|
||||
consume();
|
||||
auto token_view = StringView(m_input.characters_without_null_termination() + token_start_index, m_index - token_start_index);
|
||||
if (is_keyword(token_view))
|
||||
commit_token(Token::Type::Keyword);
|
||||
else if (is_known_type(token_view))
|
||||
commit_token(Token::Type::KnownType);
|
||||
else
|
||||
commit_token(Token::Type::Identifier);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\\' && peek(1) == '\n') {
|
||||
consume();
|
||||
consume();
|
||||
continue;
|
||||
}
|
||||
|
||||
dbgln("Unimplemented token character: {}", ch);
|
||||
emit_single_char_token(Token::Type::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<Token> Lexer::lex()
|
||||
{
|
||||
Vector<Token> tokens;
|
||||
lex_impl([&](auto token) {
|
||||
tokens.append(move(token));
|
||||
});
|
||||
return tokens;
|
||||
}
|
||||
|
||||
}
|
46
Userland/Libraries/LibGLSL/Lexer.h
Normal file
46
Userland/Libraries/LibGLSL/Lexer.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGLSL/Token.h>
|
||||
|
||||
namespace GLSL {
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
explicit Lexer(StringView, size_t start_line = 0);
|
||||
|
||||
Vector<Token> lex();
|
||||
template<typename Callback>
|
||||
void lex_iterable(Callback);
|
||||
|
||||
void set_ignore_whitespace(bool value) { m_options.ignore_whitespace = value; }
|
||||
|
||||
private:
|
||||
char peek(size_t offset = 0) const;
|
||||
char consume();
|
||||
void lex_impl(Function<void(Token)>);
|
||||
|
||||
StringView m_input;
|
||||
size_t m_index { 0 };
|
||||
Position m_previous_position { 0, 0 };
|
||||
Position m_position { 0, 0 };
|
||||
|
||||
struct Options {
|
||||
bool ignore_whitespace { false };
|
||||
} m_options;
|
||||
};
|
||||
|
||||
template<typename Callback>
|
||||
void Lexer::lex_iterable(Callback callback)
|
||||
{
|
||||
return lex_impl(move(callback));
|
||||
}
|
||||
|
||||
}
|
1142
Userland/Libraries/LibGLSL/Parser.cpp
Normal file
1142
Userland/Libraries/LibGLSL/Parser.cpp
Normal file
File diff suppressed because it is too large
Load diff
153
Userland/Libraries/LibGLSL/Parser.h
Normal file
153
Userland/Libraries/LibGLSL/Parser.h
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
* Copyright (c) 2023, Volodymyr V. <vvmposeydon@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibCodeComprehension/Types.h>
|
||||
#include <LibGLSL/AST.h>
|
||||
#include <LibGLSL/Lexer.h>
|
||||
#include <LibGLSL/Preprocessor.h>
|
||||
|
||||
namespace GLSL {
|
||||
|
||||
class Parser final {
|
||||
AK_MAKE_NONCOPYABLE(Parser);
|
||||
|
||||
public:
|
||||
explicit Parser(Vector<Token> tokens, String const& filename);
|
||||
~Parser() = default;
|
||||
|
||||
ErrorOr<NonnullRefPtr<TranslationUnit>> parse();
|
||||
bool eof() const;
|
||||
|
||||
RefPtr<TranslationUnit const> root_node() const { return m_root_node; }
|
||||
void print_tokens() const;
|
||||
Vector<Token> const& tokens() const { return m_tokens; }
|
||||
Vector<String> const& errors() const { return m_errors; }
|
||||
|
||||
private:
|
||||
enum class DeclarationType {
|
||||
Function,
|
||||
Variable,
|
||||
Struct,
|
||||
};
|
||||
ErrorOr<Optional<DeclarationType>> match_declaration_in_translation_unit();
|
||||
|
||||
ErrorOr<bool> match_struct_declaration();
|
||||
ErrorOr<bool> match_function_declaration();
|
||||
ErrorOr<bool> match_variable_declaration();
|
||||
|
||||
ErrorOr<bool> match_block_statement();
|
||||
|
||||
ErrorOr<bool> match_expression();
|
||||
ErrorOr<bool> match_name();
|
||||
ErrorOr<bool> match_string_literal();
|
||||
ErrorOr<bool> match_numeric_literal();
|
||||
ErrorOr<bool> match_boolean_literal();
|
||||
|
||||
ErrorOr<bool> match_type();
|
||||
|
||||
ErrorOr<Vector<NonnullRefPtr<Declaration const>>> parse_declarations_in_translation_unit(ASTNode const& parent);
|
||||
ErrorOr<RefPtr<Declaration const>> parse_single_declaration_in_translation_unit(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<Declaration const>> parse_declaration(ASTNode const& parent, DeclarationType);
|
||||
ErrorOr<NonnullRefPtr<StructDeclaration const>> parse_struct_declaration(ASTNode const& parent);
|
||||
ErrorOr<Vector<NonnullRefPtr<Declaration const>>> parse_struct_members(StructDeclaration& parent);
|
||||
ErrorOr<NonnullRefPtr<FunctionDeclaration const>> parse_function_declaration(ASTNode const& parent);
|
||||
ErrorOr<Vector<NonnullRefPtr<Parameter const>>> parse_parameter_list(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<FunctionDefinition const>> parse_function_definition(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<VariableDeclaration const>> parse_variable_declaration(ASTNode const& parent, bool expect_semicolon = true);
|
||||
|
||||
ErrorOr<NonnullRefPtr<Statement const>> parse_statement(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<BlockStatement const>> parse_block_statement(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<IfStatement const>> parse_if_statement(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<ForStatement const>> parse_for_statement(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<ReturnStatement const>> parse_return_statement(ASTNode const& parent);
|
||||
|
||||
enum class Associativity {
|
||||
LeftToRight,
|
||||
RightToLeft
|
||||
};
|
||||
static HashMap<BinaryOp, Associativity> s_operator_associativity;
|
||||
|
||||
ErrorOr<NonnullRefPtr<Expression const>> parse_expression(ASTNode const& parent, int min_precedence = 0, Associativity associativity = Associativity::LeftToRight);
|
||||
ErrorOr<NonnullRefPtr<Expression const>> parse_unary_expression(ASTNode const& parent);
|
||||
ErrorOr<Vector<NonnullRefPtr<Expression const>>> parse_function_call_args(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<Expression const>> parse_boolean_literal(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<Expression const>> parse_numeric_literal(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<Expression const>> parse_string_literal(ASTNode const& parent);
|
||||
ErrorOr<NonnullRefPtr<Name const>> parse_name(ASTNode const& parent, bool allow_sized_name = false);
|
||||
|
||||
ErrorOr<NonnullRefPtr<Type const>> parse_type(ASTNode const& parent);
|
||||
|
||||
bool match_unary_op();
|
||||
ErrorOr<UnaryOp> consume_unary_op();
|
||||
bool match_binary_op();
|
||||
ErrorOr<BinaryOp> peek_binary_op();
|
||||
bool match_storage_qualifier();
|
||||
ErrorOr<StorageTypeQualifier> consume_storage_qualifier();
|
||||
|
||||
Token peek(size_t offset = 0) const;
|
||||
Optional<Token> peek(Token::Type) const;
|
||||
|
||||
bool match(Token::Type);
|
||||
bool match_keyword(StringView);
|
||||
bool match_preprocessor();
|
||||
|
||||
ErrorOr<Token> consume();
|
||||
ErrorOr<Token> consume(Token::Type);
|
||||
ErrorOr<Token> consume_keyword(StringView);
|
||||
ErrorOr<void> consume_preprocessor();
|
||||
|
||||
Position position() const;
|
||||
Position previous_token_end() const;
|
||||
Optional<size_t> index_of_token_at(Position pos) const;
|
||||
Vector<Token> tokens_in_range(Position start, Position end) const;
|
||||
ErrorOr<String> text_in_range(Position start, Position end) const;
|
||||
|
||||
ErrorOr<void> error(StringView message = {});
|
||||
|
||||
template<class T, class... Args>
|
||||
NonnullRefPtr<T>
|
||||
create_ast_node(ASTNode const& parent, Position const& start, Optional<Position> end, Args&&... args)
|
||||
{
|
||||
auto node = adopt_ref(*new T(&parent, start, end, m_filename, forward<Args>(args)...));
|
||||
return node;
|
||||
}
|
||||
|
||||
NonnullRefPtr<TranslationUnit>
|
||||
create_root_ast_node(Position const& start, Position end)
|
||||
{
|
||||
auto node = adopt_ref(*new TranslationUnit(nullptr, start, end, m_filename));
|
||||
m_root_node = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
DummyAstNode& get_dummy_node()
|
||||
{
|
||||
static NonnullRefPtr<DummyAstNode> dummy = adopt_ref(*new DummyAstNode(nullptr, {}, {}, {}));
|
||||
return dummy;
|
||||
}
|
||||
|
||||
struct State {
|
||||
size_t token_index { 0 };
|
||||
};
|
||||
|
||||
void save_state();
|
||||
void load_state();
|
||||
|
||||
State m_state;
|
||||
Vector<State> m_saved_states;
|
||||
|
||||
String m_filename;
|
||||
Vector<Token> m_tokens;
|
||||
RefPtr<TranslationUnit> m_root_node;
|
||||
Vector<String> m_errors;
|
||||
};
|
||||
|
||||
}
|
416
Userland/Libraries/LibGLSL/Preprocessor.cpp
Normal file
416
Userland/Libraries/LibGLSL/Preprocessor.cpp
Normal file
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
* Copyright (c) 2023, Volodymyr V. <vvmposeydon@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Preprocessor.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibGLSL/Lexer.h>
|
||||
#include <ctype.h>
|
||||
|
||||
namespace GLSL {
|
||||
Preprocessor::Preprocessor(String filename, String program)
|
||||
: m_filename(move(filename))
|
||||
, m_program(move(program))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<Vector<Token>> Preprocessor::process_and_lex()
|
||||
{
|
||||
Lexer lexer { m_program };
|
||||
lexer.set_ignore_whitespace(true);
|
||||
auto tokens = lexer.lex();
|
||||
|
||||
m_unprocessed_tokens = tokens;
|
||||
|
||||
for (size_t token_index = 0; token_index < tokens.size(); ++token_index) {
|
||||
auto& token = tokens[token_index];
|
||||
m_current_line = token.start().line;
|
||||
if (token.type() == Token::Type::PreprocessorStatement) {
|
||||
TRY(handle_preprocessor_statement(token.text()));
|
||||
m_processed_tokens.append(tokens[token_index]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_state != State::Normal)
|
||||
continue;
|
||||
|
||||
if (token.type() == Token::Type::IncludeStatement) {
|
||||
if (token_index >= tokens.size() - 1 || tokens[token_index + 1].type() != Token::Type::IncludePath)
|
||||
continue;
|
||||
handle_include_statement(tokens[token_index + 1].text());
|
||||
if (m_options.keep_include_statements) {
|
||||
m_processed_tokens.append(tokens[token_index]);
|
||||
m_processed_tokens.append(tokens[token_index + 1]);
|
||||
}
|
||||
++token_index; // Also skip IncludePath token
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type() == Token::Type::Identifier) {
|
||||
if (auto defined_value = m_definitions.find(token.text()); defined_value != m_definitions.end()) {
|
||||
auto last_substituted_token_index = TRY(do_substitution(tokens, token_index, defined_value->value));
|
||||
token_index = last_substituted_token_index;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
m_processed_tokens.append(token);
|
||||
}
|
||||
|
||||
return m_processed_tokens;
|
||||
}
|
||||
|
||||
static void consume_whitespace(GenericLexer& lexer)
|
||||
{
|
||||
auto ignore_line = [&] {
|
||||
for (;;) {
|
||||
if (lexer.consume_specific("\\\n"sv)) {
|
||||
lexer.ignore(2);
|
||||
} else {
|
||||
lexer.ignore_until('\n');
|
||||
lexer.ignore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
for (;;) {
|
||||
if (lexer.consume_specific("//"sv)) {
|
||||
ignore_line();
|
||||
} else if (lexer.consume_specific("/*"sv)) {
|
||||
lexer.ignore_until("*/");
|
||||
lexer.ignore(2);
|
||||
} else if (lexer.next_is("\\\n"sv)) {
|
||||
lexer.ignore(2);
|
||||
} else if (lexer.is_eof() || !lexer.next_is(isspace)) {
|
||||
break;
|
||||
} else {
|
||||
lexer.ignore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<void> Preprocessor::handle_preprocessor_statement(StringView line)
|
||||
{
|
||||
GenericLexer lexer(line);
|
||||
|
||||
consume_whitespace(lexer);
|
||||
lexer.consume_specific('#');
|
||||
consume_whitespace(lexer);
|
||||
auto keyword = lexer.consume_until(' ');
|
||||
lexer.ignore();
|
||||
if (keyword.is_empty() || keyword.is_null() || keyword.is_whitespace())
|
||||
return {};
|
||||
|
||||
return TRY(handle_preprocessor_keyword(keyword, lexer));
|
||||
}
|
||||
|
||||
void Preprocessor::handle_include_statement(StringView include_path)
|
||||
{
|
||||
m_included_paths.append(include_path);
|
||||
if (definitions_in_header_callback) {
|
||||
for (auto& def : definitions_in_header_callback(include_path))
|
||||
m_definitions.set(def.key, def.value);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<void> Preprocessor::handle_preprocessor_keyword(StringView keyword, GenericLexer& line_lexer)
|
||||
{
|
||||
if (keyword == "include") {
|
||||
// Should have called 'handle_include_statement'.
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (keyword == "else") {
|
||||
if (m_options.ignore_invalid_statements && m_current_depth == 0)
|
||||
return {};
|
||||
VERIFY(m_current_depth > 0);
|
||||
if (m_depths_of_not_taken_branches.contains_slow(m_current_depth - 1)) {
|
||||
m_depths_of_not_taken_branches.remove_all_matching([this](auto x) { return x == m_current_depth - 1; });
|
||||
m_state = State::Normal;
|
||||
}
|
||||
if (m_depths_of_taken_branches.contains_slow(m_current_depth - 1)) {
|
||||
m_state = State::SkipElseBranch;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
if (keyword == "endif") {
|
||||
if (m_options.ignore_invalid_statements && m_current_depth == 0)
|
||||
return {};
|
||||
VERIFY(m_current_depth > 0);
|
||||
--m_current_depth;
|
||||
if (m_depths_of_not_taken_branches.contains_slow(m_current_depth)) {
|
||||
m_depths_of_not_taken_branches.remove_all_matching([this](auto x) { return x == m_current_depth; });
|
||||
}
|
||||
if (m_depths_of_taken_branches.contains_slow(m_current_depth)) {
|
||||
m_depths_of_taken_branches.remove_all_matching([this](auto x) { return x == m_current_depth; });
|
||||
}
|
||||
m_state = State::Normal;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (keyword == "define") {
|
||||
if (m_state == State::Normal) {
|
||||
auto definition = TRY(create_definition(line_lexer.consume_all()));
|
||||
if (definition.has_value())
|
||||
m_definitions.set(definition->key, *definition);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (keyword == "undef") {
|
||||
if (m_state == State::Normal) {
|
||||
auto key = line_lexer.consume_until(' ');
|
||||
line_lexer.consume_all();
|
||||
m_definitions.remove(key);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (keyword == "ifdef") {
|
||||
++m_current_depth;
|
||||
if (m_state == State::Normal) {
|
||||
auto key = line_lexer.consume_until(' ');
|
||||
line_lexer.ignore();
|
||||
if (m_definitions.contains(key)) {
|
||||
m_depths_of_taken_branches.append(m_current_depth - 1);
|
||||
return {};
|
||||
} else {
|
||||
m_depths_of_not_taken_branches.append(m_current_depth - 1);
|
||||
m_state = State::SkipIfBranch;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (keyword == "ifndef") {
|
||||
++m_current_depth;
|
||||
if (m_state == State::Normal) {
|
||||
auto key = line_lexer.consume_until(' ');
|
||||
line_lexer.ignore();
|
||||
if (!m_definitions.contains(key)) {
|
||||
m_depths_of_taken_branches.append(m_current_depth - 1);
|
||||
return {};
|
||||
} else {
|
||||
m_depths_of_not_taken_branches.append(m_current_depth - 1);
|
||||
m_state = State::SkipIfBranch;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (keyword == "if") {
|
||||
++m_current_depth;
|
||||
if (m_state == State::Normal) {
|
||||
// FIXME: Implement #if logic
|
||||
// We currently always take #if branches.
|
||||
m_depths_of_taken_branches.append(m_current_depth - 1);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
if (keyword == "elif") {
|
||||
if (m_options.ignore_invalid_statements && m_current_depth == 0)
|
||||
return {};
|
||||
VERIFY(m_current_depth > 0);
|
||||
// FIXME: Evaluate the elif expression
|
||||
// We currently always treat the expression in #elif as true.
|
||||
if (m_depths_of_not_taken_branches.contains_slow(m_current_depth - 1) /* && should_take*/) {
|
||||
m_depths_of_not_taken_branches.remove_all_matching([this](auto x) { return x == m_current_depth - 1; });
|
||||
m_state = State::Normal;
|
||||
}
|
||||
if (m_depths_of_taken_branches.contains_slow(m_current_depth - 1)) {
|
||||
m_state = State::SkipElseBranch;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (keyword == "pragma") {
|
||||
line_lexer.consume_all();
|
||||
return {};
|
||||
}
|
||||
if (keyword == "error") {
|
||||
line_lexer.consume_all();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!m_options.ignore_unsupported_keywords) {
|
||||
dbgln("Unsupported preprocessor keyword: {}", keyword);
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<size_t> Preprocessor::do_substitution(Vector<Token> const& tokens, size_t token_index, Definition const& defined_value)
|
||||
{
|
||||
if (defined_value.value.is_empty())
|
||||
return token_index;
|
||||
|
||||
Substitution sub;
|
||||
sub.defined_value = defined_value;
|
||||
|
||||
auto macro_call = parse_macro_call(tokens, token_index);
|
||||
|
||||
if (!macro_call.has_value())
|
||||
return token_index;
|
||||
|
||||
Vector<Token> original_tokens;
|
||||
for (size_t i = token_index; i <= macro_call->end_token_index; ++i) {
|
||||
original_tokens.append(tokens[i]);
|
||||
}
|
||||
VERIFY(!original_tokens.is_empty());
|
||||
|
||||
auto processed_value = TRY(evaluate_macro_call(*macro_call, defined_value));
|
||||
m_substitutions.append({ original_tokens, defined_value, processed_value });
|
||||
|
||||
Lexer lexer(processed_value);
|
||||
lexer.lex_iterable([&](auto token) {
|
||||
if (token.type() == Token::Type::Whitespace)
|
||||
return;
|
||||
token.set_start(original_tokens.first().start());
|
||||
token.set_end(original_tokens.first().end());
|
||||
m_processed_tokens.append(token);
|
||||
});
|
||||
return macro_call->end_token_index;
|
||||
}
|
||||
|
||||
Optional<Preprocessor::MacroCall> Preprocessor::parse_macro_call(Vector<Token> const& tokens, size_t token_index)
|
||||
{
|
||||
auto name = tokens[token_index];
|
||||
++token_index;
|
||||
|
||||
if (token_index >= tokens.size() || tokens[token_index].type() != Token::Type::LeftParen)
|
||||
return MacroCall { name, {}, token_index - 1 };
|
||||
++token_index;
|
||||
|
||||
Vector<MacroCall::Argument> arguments;
|
||||
Optional<MacroCall::Argument> current_argument;
|
||||
|
||||
size_t paren_depth = 1;
|
||||
for (; token_index < tokens.size(); ++token_index) {
|
||||
auto& token = tokens[token_index];
|
||||
if (token.type() == Token::Type::LeftParen)
|
||||
++paren_depth;
|
||||
if (token.type() == Token::Type::RightParen)
|
||||
--paren_depth;
|
||||
|
||||
if (paren_depth == 0) {
|
||||
if (current_argument.has_value())
|
||||
arguments.append(*current_argument);
|
||||
break;
|
||||
}
|
||||
|
||||
if (paren_depth == 1 && token.type() == Token::Type::Comma) {
|
||||
if (current_argument.has_value())
|
||||
arguments.append(*current_argument);
|
||||
current_argument = {};
|
||||
} else {
|
||||
if (!current_argument.has_value())
|
||||
current_argument = MacroCall::Argument {};
|
||||
current_argument->tokens.append(token);
|
||||
}
|
||||
}
|
||||
|
||||
if (token_index >= tokens.size())
|
||||
return {};
|
||||
|
||||
return MacroCall { name, move(arguments), token_index };
|
||||
}
|
||||
|
||||
ErrorOr<Optional<Preprocessor::Definition>> Preprocessor::create_definition(StringView line)
|
||||
{
|
||||
Lexer lexer { line };
|
||||
lexer.set_ignore_whitespace(true);
|
||||
auto tokens = lexer.lex();
|
||||
if (tokens.is_empty())
|
||||
return Optional<Preprocessor::Definition> {};
|
||||
|
||||
if (tokens.first().type() != Token::Type::Identifier)
|
||||
return Optional<Preprocessor::Definition> {};
|
||||
|
||||
Definition definition;
|
||||
definition.filename = m_filename;
|
||||
definition.line = m_current_line;
|
||||
|
||||
definition.key = tokens.first().text();
|
||||
|
||||
if (tokens.size() == 1)
|
||||
return definition;
|
||||
|
||||
size_t token_index = 1;
|
||||
// Parse macro parameters (if any)
|
||||
if (tokens[token_index].type() == Token::Type::LeftParen) {
|
||||
++token_index;
|
||||
while (token_index < tokens.size() && tokens[token_index].type() != Token::Type::RightParen) {
|
||||
auto param = tokens[token_index];
|
||||
if (param.type() != Token::Type::Identifier)
|
||||
return Optional<Preprocessor::Definition> {};
|
||||
|
||||
if (token_index + 1 >= tokens.size())
|
||||
return Optional<Preprocessor::Definition> {};
|
||||
|
||||
++token_index;
|
||||
|
||||
if (tokens[token_index].type() == Token::Type::Comma)
|
||||
++token_index;
|
||||
else if (tokens[token_index].type() != Token::Type::RightParen)
|
||||
return Optional<Preprocessor::Definition> {};
|
||||
|
||||
definition.parameters.empend(param.text());
|
||||
}
|
||||
if (token_index >= tokens.size())
|
||||
return Optional<Preprocessor::Definition> {};
|
||||
++token_index;
|
||||
}
|
||||
|
||||
if (token_index < tokens.size())
|
||||
definition.value = TRY(remove_escaped_newlines(line.substring_view(tokens[token_index].start().column))).bytes_as_string_view();
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
ErrorOr<String> Preprocessor::remove_escaped_newlines(StringView value)
|
||||
{
|
||||
static constexpr auto escaped_newline = "\\\n"sv;
|
||||
AK::StringBuilder processed_value;
|
||||
GenericLexer lexer { value };
|
||||
while (!lexer.is_eof()) {
|
||||
processed_value.append(lexer.consume_until(escaped_newline));
|
||||
lexer.ignore(escaped_newline.length());
|
||||
}
|
||||
return processed_value.to_string();
|
||||
}
|
||||
|
||||
ErrorOr<String> Preprocessor::evaluate_macro_call(MacroCall const& macro_call, Definition const& definition)
|
||||
{
|
||||
if (macro_call.arguments.size() != definition.parameters.size()) {
|
||||
dbgln("mismatch in # of arguments for macro call: {}", macro_call.name.text());
|
||||
return String {};
|
||||
}
|
||||
|
||||
Lexer lexer { definition.value };
|
||||
StringBuilder processed_value;
|
||||
lexer.lex_iterable([&](auto token) {
|
||||
if (token.type() != Token::Type::Identifier) {
|
||||
processed_value.append(token.text());
|
||||
return;
|
||||
}
|
||||
|
||||
auto param_index = definition.parameters.find_first_index(token.text());
|
||||
if (!param_index.has_value()) {
|
||||
processed_value.append(token.text());
|
||||
return;
|
||||
}
|
||||
|
||||
auto& argument = macro_call.arguments[*param_index];
|
||||
for (auto& arg_token : argument.tokens) {
|
||||
processed_value.append(arg_token.text());
|
||||
}
|
||||
});
|
||||
|
||||
return processed_value.to_string();
|
||||
}
|
||||
|
||||
};
|
103
Userland/Libraries/LibGLSL/Preprocessor.h
Normal file
103
Userland/Libraries/LibGLSL/Preprocessor.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
|
||||
* Copyright (c) 2023, Volodymyr V. <vvmposeydon@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGLSL/Token.h>
|
||||
|
||||
namespace GLSL {
|
||||
|
||||
class Preprocessor {
|
||||
|
||||
public:
|
||||
explicit Preprocessor(String filename, String program);
|
||||
ErrorOr<Vector<Token>> process_and_lex();
|
||||
Vector<StringView> included_paths() const { return m_included_paths; }
|
||||
|
||||
struct Definition {
|
||||
StringView key;
|
||||
Vector<StringView> parameters;
|
||||
StringView value;
|
||||
FlyString filename;
|
||||
size_t line { 0 };
|
||||
size_t column { 0 };
|
||||
};
|
||||
using Definitions = HashMap<StringView, Definition>;
|
||||
|
||||
struct Substitution {
|
||||
Vector<Token> original_tokens;
|
||||
Definition defined_value;
|
||||
String processed_value;
|
||||
};
|
||||
|
||||
Definitions const& definitions() const { return m_definitions; }
|
||||
Vector<Substitution> const& substitutions() const { return m_substitutions; }
|
||||
|
||||
void set_ignore_unsupported_keywords(bool ignore) { m_options.ignore_unsupported_keywords = ignore; }
|
||||
void set_ignore_invalid_statements(bool ignore) { m_options.ignore_invalid_statements = ignore; }
|
||||
void set_keep_include_statements(bool keep) { m_options.keep_include_statements = keep; }
|
||||
|
||||
Function<Definitions(StringView)> definitions_in_header_callback { nullptr };
|
||||
|
||||
Vector<Token> const& unprocessed_tokens() const { return m_unprocessed_tokens; }
|
||||
|
||||
private:
|
||||
ErrorOr<void> handle_preprocessor_statement(StringView);
|
||||
void handle_include_statement(StringView);
|
||||
ErrorOr<void> handle_preprocessor_keyword(StringView keyword, GenericLexer& line_lexer);
|
||||
ErrorOr<String> remove_escaped_newlines(StringView value);
|
||||
|
||||
ErrorOr<size_t> do_substitution(Vector<Token> const& tokens, size_t token_index, Definition const&);
|
||||
ErrorOr<Optional<Definition>> create_definition(StringView line);
|
||||
|
||||
struct MacroCall {
|
||||
Token name;
|
||||
struct Argument {
|
||||
Vector<Token> tokens;
|
||||
};
|
||||
Vector<Argument> arguments;
|
||||
size_t end_token_index { 0 };
|
||||
};
|
||||
Optional<MacroCall> parse_macro_call(Vector<Token> const& tokens, size_t token_index);
|
||||
ErrorOr<String> evaluate_macro_call(MacroCall const&, Definition const&);
|
||||
|
||||
String m_filename;
|
||||
String m_program;
|
||||
|
||||
Vector<Token> m_unprocessed_tokens;
|
||||
Vector<Token> m_processed_tokens;
|
||||
Definitions m_definitions;
|
||||
Vector<Substitution> m_substitutions;
|
||||
|
||||
size_t m_current_line { 0 };
|
||||
size_t m_current_depth { 0 };
|
||||
Vector<size_t> m_depths_of_taken_branches;
|
||||
Vector<size_t> m_depths_of_not_taken_branches;
|
||||
|
||||
enum class State {
|
||||
Normal,
|
||||
SkipIfBranch,
|
||||
SkipElseBranch
|
||||
};
|
||||
State m_state { State::Normal };
|
||||
|
||||
Vector<StringView> m_included_paths;
|
||||
|
||||
struct Options {
|
||||
bool ignore_unsupported_keywords { false };
|
||||
bool ignore_invalid_statements { false };
|
||||
bool keep_include_statements { false };
|
||||
} m_options;
|
||||
};
|
||||
}
|
44
Userland/Libraries/LibGLSL/Token.cpp
Normal file
44
Userland/Libraries/LibGLSL/Token.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Token.h"
|
||||
#include <AK/String.h>
|
||||
|
||||
namespace GLSL {
|
||||
|
||||
bool Position::operator<(Position const& other) const
|
||||
{
|
||||
return line < other.line || (line == other.line && column < other.column);
|
||||
}
|
||||
|
||||
bool Position::operator>(Position const& other) const
|
||||
{
|
||||
return !(*this < other) && !(*this == other);
|
||||
}
|
||||
|
||||
bool Position::operator==(Position const& other) const
|
||||
{
|
||||
return line == other.line && column == other.column;
|
||||
}
|
||||
|
||||
bool Position::operator<=(Position const& other) const
|
||||
{
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
ErrorOr<String> Token::to_string() const
|
||||
{
|
||||
return String::formatted("{} {}:{}-{}:{} ({})", type_to_string(m_type), start().line, start().column, end().line, end().column, text());
|
||||
}
|
||||
|
||||
ErrorOr<String> Token::type_as_string() const
|
||||
{
|
||||
auto str = type_to_string(m_type);
|
||||
auto view = StringView(str, strlen(str));
|
||||
return String::from_utf8(view);
|
||||
}
|
||||
|
||||
}
|
133
Userland/Libraries/LibGLSL/Token.h
Normal file
133
Userland/Libraries/LibGLSL/Token.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright (c) 2023, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace GLSL {
|
||||
|
||||
#define FOR_EACH_TOKEN_TYPE \
|
||||
__TOKEN(Unknown) \
|
||||
__TOKEN(Whitespace) \
|
||||
__TOKEN(PreprocessorStatement) \
|
||||
__TOKEN(IncludeStatement) \
|
||||
__TOKEN(IncludePath) \
|
||||
__TOKEN(LeftParen) \
|
||||
__TOKEN(RightParen) \
|
||||
__TOKEN(LeftCurly) \
|
||||
__TOKEN(RightCurly) \
|
||||
__TOKEN(LeftBracket) \
|
||||
__TOKEN(RightBracket) \
|
||||
__TOKEN(Less) \
|
||||
__TOKEN(Greater) \
|
||||
__TOKEN(LessEquals) \
|
||||
__TOKEN(GreaterEquals) \
|
||||
__TOKEN(LessLess) \
|
||||
__TOKEN(GreaterGreater) \
|
||||
__TOKEN(LessLessEquals) \
|
||||
__TOKEN(GreaterGreaterEquals) \
|
||||
__TOKEN(Comma) \
|
||||
__TOKEN(Plus) \
|
||||
__TOKEN(PlusPlus) \
|
||||
__TOKEN(PlusEquals) \
|
||||
__TOKEN(Minus) \
|
||||
__TOKEN(MinusMinus) \
|
||||
__TOKEN(MinusEquals) \
|
||||
__TOKEN(Asterisk) \
|
||||
__TOKEN(AsteriskEquals) \
|
||||
__TOKEN(Slash) \
|
||||
__TOKEN(SlashEquals) \
|
||||
__TOKEN(Percent) \
|
||||
__TOKEN(PercentEquals) \
|
||||
__TOKEN(Caret) \
|
||||
__TOKEN(CaretCaret) \
|
||||
__TOKEN(CaretEquals) \
|
||||
__TOKEN(ExclamationMark) \
|
||||
__TOKEN(ExclamationMarkEquals) \
|
||||
__TOKEN(Equals) \
|
||||
__TOKEN(EqualsEquals) \
|
||||
__TOKEN(And) \
|
||||
__TOKEN(AndAnd) \
|
||||
__TOKEN(AndEquals) \
|
||||
__TOKEN(Pipe) \
|
||||
__TOKEN(PipePipe) \
|
||||
__TOKEN(PipeEquals) \
|
||||
__TOKEN(Tilde) \
|
||||
__TOKEN(QuestionMark) \
|
||||
__TOKEN(Colon) \
|
||||
__TOKEN(Semicolon) \
|
||||
__TOKEN(Dot) \
|
||||
__TOKEN(DoubleQuotedString) \
|
||||
__TOKEN(SingleQuotedString) \
|
||||
__TOKEN(RawString) \
|
||||
__TOKEN(EscapeSequence) \
|
||||
__TOKEN(Integer) \
|
||||
__TOKEN(Float) \
|
||||
__TOKEN(Keyword) \
|
||||
__TOKEN(KnownType) \
|
||||
__TOKEN(Identifier) \
|
||||
__TOKEN(EOF_TOKEN)
|
||||
|
||||
struct Position {
|
||||
size_t line { 0 };
|
||||
size_t column { 0 };
|
||||
|
||||
bool operator<(Position const&) const;
|
||||
bool operator<=(Position const&) const;
|
||||
bool operator>(Position const&) const;
|
||||
bool operator==(Position const&) const;
|
||||
};
|
||||
|
||||
struct Token {
|
||||
enum class Type {
|
||||
#define __TOKEN(x) x,
|
||||
FOR_EACH_TOKEN_TYPE
|
||||
#undef __TOKEN
|
||||
};
|
||||
|
||||
Token(Type type, Position const& start, Position const& end, StringView text)
|
||||
: m_type(type)
|
||||
, m_start(start)
|
||||
, m_end(end)
|
||||
, m_text(text)
|
||||
{
|
||||
}
|
||||
|
||||
static char const* type_to_string(Type t)
|
||||
{
|
||||
switch (t) {
|
||||
#define __TOKEN(x) \
|
||||
case Type::x: \
|
||||
return #x;
|
||||
FOR_EACH_TOKEN_TYPE
|
||||
#undef __TOKEN
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<String> to_string() const;
|
||||
ErrorOr<String> type_as_string() const;
|
||||
|
||||
Position const& start() const { return m_start; }
|
||||
Position const& end() const { return m_end; }
|
||||
|
||||
void set_start(Position const& other) { m_start = other; }
|
||||
void set_end(Position const& other) { m_end = other; }
|
||||
Type type() const { return m_type; }
|
||||
StringView text() const { return m_text; }
|
||||
|
||||
private:
|
||||
Type m_type { Type::Unknown };
|
||||
Position m_start;
|
||||
Position m_end;
|
||||
StringView m_text;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#undef FOR_EACH_TOKEN_TYPE
|
Loading…
Add table
Reference in a new issue