Reland "Add writer to emit the AST."
This reverts commit 5e5655155110e121763e3dca13d20c3eda09167e.
Change-Id: I2c3da583090ebef87cc71a743746d741345847c7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/122782
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 66e2dcd..59776f9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -157,6 +157,7 @@
option_if_not_defined(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ${DAWN_ENABLE_VULKAN})
option_if_not_defined(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
+option_if_not_defined(TINT_BUILD_SYNTAX_TREE_WRITER "Build the syntax tree writer" OFF)
option_if_not_defined(TINT_BUILD_IR "Build the IR" ON)
option_if_not_defined(TINT_BUILD_FUZZERS "Build fuzzers" OFF)
@@ -292,6 +293,7 @@
message(STATUS "Tint build MSL writer: ${TINT_BUILD_MSL_WRITER}")
message(STATUS "Tint build SPIR-V writer: ${TINT_BUILD_SPV_WRITER}")
message(STATUS "Tint build WGSL writer: ${TINT_BUILD_WGSL_WRITER}")
+message(STATUS "Tint build Syntax Tree writer: ${TINT_BUILD_SYNTAX_TREE_WRITER}")
message(STATUS "Tint build IR: ${TINT_BUILD_IR}")
message(STATUS "Tint build fuzzers: ${TINT_BUILD_FUZZERS}")
message(STATUS "Tint build SPIRV-Tools fuzzer: ${TINT_BUILD_SPIRV_TOOLS_FUZZER}")
@@ -521,6 +523,8 @@
target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_MSL_WRITER=$<BOOL:${TINT_BUILD_MSL_WRITER}>)
target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_SPV_WRITER=$<BOOL:${TINT_BUILD_SPV_WRITER}>)
target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_WGSL_WRITER=$<BOOL:${TINT_BUILD_WGSL_WRITER}>)
+ target_compile_definitions(${TARGET} PUBLIC
+ -DTINT_BUILD_SYNTAX_TREE_WRITER=$<BOOL:${TINT_BUILD_SYNTAX_TREE_WRITER}>)
target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_IR=$<BOOL:${TINT_BUILD_IR}>)
common_compile_options(${TARGET})
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 6479684..3098e1c 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -79,6 +79,12 @@
defines += [ "TINT_BUILD_GLSL_WRITER=0" ]
}
+ if (tint_build_syntax_tree_writer) {
+ defines += [ "TINT_BUILD_SYNTAX_TREE_WRITER=1" ]
+ } else {
+ defines += [ "TINT_BUILD_SYNTAX_TREE_WRITER=0" ]
+ }
+
include_dirs = [
"${tint_root_dir}/",
"${tint_root_dir}/include/",
@@ -1058,6 +1064,25 @@
]
}
+libtint_source_set("libtint_syntax_tree_writer_src") {
+ sources = [
+ "writer/syntax_tree/generator.cc",
+ "writer/syntax_tree/generator.h",
+ "writer/syntax_tree/generator_impl.cc",
+ "writer/syntax_tree/generator_impl.h",
+ ]
+
+ deps = [
+ ":libtint_ast_src",
+ ":libtint_base_src",
+ ":libtint_builtins_src",
+ ":libtint_program_src",
+ ":libtint_sem_src",
+ ":libtint_type_src",
+ ":libtint_writer_src",
+ ]
+}
+
source_set("libtint") {
public_deps = [
":libtint_ast_src",
@@ -1102,6 +1127,10 @@
public_deps += [ ":libtint_glsl_writer_src" ]
}
+ if (tint_build_syntax_tree_writer) {
+ public_deps += [ ":libtint_syntax_tree_writer_src" ]
+ }
+
configs += [ ":tint_common_config" ]
public_configs = [ ":tint_public_config" ]
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 6a8f503..7f23d23 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -677,6 +677,15 @@
)
endif()
+if(${TINT_BUILD_SYNTAX_TREE_WRITER})
+ list(APPEND TINT_LIB_SRCS
+ writer/syntax_tree/generator.cc
+ writer/syntax_tree/generator.h
+ writer/syntax_tree/generator_impl.cc
+ writer/syntax_tree/generator_impl.h
+ )
+endif()
+
if(${TINT_BUILD_IR})
list(APPEND TINT_LIB_SRCS
ir/binary.cc
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index 0b2aad2..3d6318d 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -29,6 +29,11 @@
#include "glslang/Public/ShaderLang.h"
#endif // TINT_BUILD_GLSL_WRITER
+#if TINT_BUILD_SYNTAX_TREE_WRITER
+#include "src/tint/writer/syntax_tree/generator.h" // nogncheck
+
+#endif // TINT_BUILD_SYNTAX_TREE_WRITER
+
#if TINT_BUILD_SPV_READER
#include "spirv-tools/libspirv.hpp"
#endif // TINT_BUILD_SPV_READER
@@ -106,6 +111,10 @@
bool dump_ir = false;
bool dump_ir_graph = false;
#endif // TINT_BUILD_IR
+
+#if TINT_BUILD_SYNTAX_TREE_WRITER
+ bool dump_syntax_tree = false;
+#endif // TINB_BUILD_SYNTAX_TREE_WRITER
};
const char kUsage[] = R"(Usage: tint [options] <input-file>
@@ -383,6 +392,10 @@
} else if (arg == "--dump-ir-graph") {
opts->dump_ir_graph = true;
#endif // TINT_BUILD_IR
+#if TINT_BUILD_SYNTAX_TREE_WRITER
+ } else if (arg == "--dump-ast") {
+ opts->dump_syntax_tree = true;
+#endif // TINT_BUILD_SYNTAX_TREE_WRITER
} else if (arg == "--xcrun") {
++i;
if (i >= args.size()) {
@@ -1055,6 +1068,9 @@
" --dump-ir -- Writes the IR to stdout\n"
" --dump-ir-graph -- Writes the IR graph to 'tint.dot' as a dot graph\n";
#endif // TINT_BUILD_IR
+#if TINT_BUILD_SYNTAX_TREE_WRITER
+ usage += " --dump-ast -- Writes the AST to stdout\n";
+#endif // TINT_BUILD_SYNTAX_TREE_WRITER
std::cout << usage << std::endl;
return 0;
@@ -1092,6 +1108,18 @@
return 1;
}
+#if TINT_BUILD_SYNTAX_TREE_WRITER
+ if (options.dump_syntax_tree) {
+ tint::writer::syntax_tree::Options gen_options;
+ auto result = tint::writer::syntax_tree::Generate(program.get(), gen_options);
+ if (!result.success) {
+ std::cerr << "Failed to dump AST: " << result.error << std::endl;
+ } else {
+ std::cout << result.ast << std::endl;
+ }
+ }
+#endif // TINT_BUILD_SYNTAX_TREE_WRITER
+
#if TINT_BUILD_IR
if (options.dump_ir || options.dump_ir_graph) {
auto result = tint::ir::Module::FromProgram(program.get());
diff --git a/src/tint/writer/syntax_tree/generator.cc b/src/tint/writer/syntax_tree/generator.cc
new file mode 100644
index 0000000..6919d24
--- /dev/null
+++ b/src/tint/writer/syntax_tree/generator.cc
@@ -0,0 +1,36 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/writer/syntax_tree/generator.h"
+#include "src/tint/writer/syntax_tree/generator_impl.h"
+
+namespace tint::writer::syntax_tree {
+
+Result::Result() = default;
+Result::~Result() = default;
+Result::Result(const Result&) = default;
+
+Result Generate(const Program* program, const Options&) {
+ Result result;
+
+ // Generate the AST dump.
+ auto impl = std::make_unique<GeneratorImpl>(program);
+ result.success = impl->Generate();
+ result.error = impl->error();
+ result.ast = impl->result();
+
+ return result;
+}
+
+} // namespace tint::writer::syntax_tree
diff --git a/src/tint/writer/syntax_tree/generator.h b/src/tint/writer/syntax_tree/generator.h
new file mode 100644
index 0000000..ce81c3f
--- /dev/null
+++ b/src/tint/writer/syntax_tree/generator.h
@@ -0,0 +1,65 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_WRITER_SYNTAX_TREE_GENERATOR_H_
+#define SRC_TINT_WRITER_SYNTAX_TREE_GENERATOR_H_
+
+#include <memory>
+#include <string>
+
+#include "src/tint/writer/text.h"
+
+// Forward declarations
+namespace tint {
+class Program;
+} // namespace tint
+
+namespace tint::writer::syntax_tree {
+
+class GeneratorImpl;
+
+/// Configuration options used for generating AST.
+struct Options {};
+
+/// The result produced when generating AST.
+struct Result {
+ /// Constructor
+ Result();
+
+ /// Destructor
+ ~Result();
+
+ /// Copy constructor
+ Result(const Result&);
+
+ /// True if generation was successful.
+ bool success = false;
+
+ /// The errors generated during code generation, if any.
+ std::string error;
+
+ /// The generated AST.
+ std::string ast = "";
+};
+
+/// Generate an AST dump for a program, according to a set of configuration options.
+/// The result will contain the AST, as well as success status and diagnostic information.
+/// @param program the program to dump
+/// @param options the configuration options to use when dumping
+/// @returns the resulting AST dump and supplementary information
+Result Generate(const Program* program, const Options& options);
+
+} // namespace tint::writer::syntax_tree
+
+#endif // SRC_TINT_WRITER_SYNTAX_TREE_GENERATOR_H_
diff --git a/src/tint/writer/syntax_tree/generator_impl.cc b/src/tint/writer/syntax_tree/generator_impl.cc
new file mode 100644
index 0000000..e5e2f2f
--- /dev/null
+++ b/src/tint/writer/syntax_tree/generator_impl.cc
@@ -0,0 +1,1349 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/writer/syntax_tree/generator_impl.h"
+
+#include <algorithm>
+
+#include "src/tint/ast/alias.h"
+#include "src/tint/ast/bool_literal_expression.h"
+#include "src/tint/ast/call_statement.h"
+#include "src/tint/ast/float_literal_expression.h"
+#include "src/tint/ast/id_attribute.h"
+#include "src/tint/ast/internal_attribute.h"
+#include "src/tint/ast/interpolate_attribute.h"
+#include "src/tint/ast/invariant_attribute.h"
+#include "src/tint/ast/module.h"
+#include "src/tint/ast/stage_attribute.h"
+#include "src/tint/ast/stride_attribute.h"
+#include "src/tint/ast/struct_member_align_attribute.h"
+#include "src/tint/ast/struct_member_offset_attribute.h"
+#include "src/tint/ast/struct_member_size_attribute.h"
+#include "src/tint/ast/variable_decl_statement.h"
+#include "src/tint/ast/workgroup_attribute.h"
+#include "src/tint/sem/struct.h"
+#include "src/tint/sem/switch_statement.h"
+#include "src/tint/utils/math.h"
+#include "src/tint/utils/scoped_assignment.h"
+#include "src/tint/writer/float_to_string.h"
+
+namespace tint::writer::syntax_tree {
+
+GeneratorImpl::GeneratorImpl(const Program* program) : TextGenerator(program) {}
+
+GeneratorImpl::~GeneratorImpl() = default;
+
+bool GeneratorImpl::Generate() {
+ // Generate global declarations in the order they appear in the module.
+ for (auto* decl : program_->AST().GlobalDeclarations()) {
+ if (!Switch(
+ decl, //
+ [&](const ast::DiagnosticDirective* dd) {
+ return EmitDiagnosticControl(dd->control);
+ },
+ [&](const ast::Enable* e) { return EmitEnable(e); },
+ [&](const ast::TypeDecl* td) { return EmitTypeDecl(td); },
+ [&](const ast::Function* func) { return EmitFunction(func); },
+ [&](const ast::Variable* var) { return EmitVariable(var); },
+ [&](const ast::ConstAssert* ca) { return EmitConstAssert(ca); },
+ [&](Default) {
+ TINT_UNREACHABLE(Writer, diagnostics_);
+ return false;
+ })) {
+ return false;
+ }
+ if (decl != program_->AST().GlobalDeclarations().Back()) {
+ line();
+ }
+ }
+
+ return true;
+}
+
+bool GeneratorImpl::EmitDiagnosticControl(const ast::DiagnosticControl& diagnostic) {
+ line() << "DiagnosticControl [severity: " << diagnostic.severity
+ << ", rule: " << program_->Symbols().NameFor(diagnostic.rule_name->symbol) << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitEnable(const ast::Enable* enable) {
+ line() << "Enable [" << enable->extension << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitTypeDecl(const ast::TypeDecl* ty) {
+ return Switch(
+ ty,
+ [&](const ast::Alias* alias) { //
+ line() << "Alias [";
+ {
+ ScopedIndent ai(this);
+
+ line() << "name: " << program_->Symbols().NameFor(alias->name->symbol);
+ line() << "expr: ";
+ {
+ ScopedIndent ex(this);
+ if (!EmitExpression(alias->type)) {
+ return false;
+ }
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::Struct* str) { //
+ return EmitStructType(str);
+ },
+ [&](Default) { //
+ diagnostics_.add_error(diag::System::Writer,
+ "unknown declared type: " + std::string(ty->TypeInfo().name));
+ return false;
+ });
+}
+
+bool GeneratorImpl::EmitExpression(const ast::Expression* expr) {
+ return Switch(
+ expr,
+ [&](const ast::IndexAccessorExpression* a) { //
+ return EmitIndexAccessor(a);
+ },
+ [&](const ast::BinaryExpression* b) { //
+ return EmitBinary(b);
+ },
+ [&](const ast::BitcastExpression* b) { //
+ return EmitBitcast(b);
+ },
+ [&](const ast::CallExpression* c) { //
+ return EmitCall(c);
+ },
+ [&](const ast::IdentifierExpression* i) { //
+ return EmitIdentifier(i);
+ },
+ [&](const ast::LiteralExpression* l) { //
+ return EmitLiteral(l);
+ },
+ [&](const ast::MemberAccessorExpression* m) { //
+ return EmitMemberAccessor(m);
+ },
+ [&](const ast::PhonyExpression*) { //
+ line() << "[PhonyExpression]";
+ return true;
+ },
+ [&](const ast::UnaryOpExpression* u) { //
+ return EmitUnaryOp(u);
+ },
+ [&](Default) {
+ diagnostics_.add_error(diag::System::Writer, "unknown expression type");
+ return false;
+ });
+}
+
+bool GeneratorImpl::EmitIndexAccessor(const ast::IndexAccessorExpression* expr) {
+ line() << "IndexAccessorExpression [";
+ {
+ ScopedIndent iae(this);
+ line() << "object: ";
+ {
+ ScopedIndent obj(this);
+ if (!EmitExpression(expr->object)) {
+ return false;
+ }
+ }
+
+ line() << "index: ";
+ {
+ ScopedIndent idx(this);
+ if (!EmitExpression(expr->index)) {
+ return false;
+ }
+ }
+ }
+ line() << "]";
+
+ return true;
+}
+
+bool GeneratorImpl::EmitMemberAccessor(const ast::MemberAccessorExpression* expr) {
+ line() << "MemberAccessorExpression [";
+ {
+ ScopedIndent mae(this);
+
+ line() << "object: ";
+ {
+ ScopedIndent obj(this);
+ if (!EmitExpression(expr->object)) {
+ return false;
+ }
+ }
+ line() << "member: " << program_->Symbols().NameFor(expr->member->symbol);
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitBitcast(const ast::BitcastExpression* expr) {
+ line() << "BitcastExpression [";
+ {
+ ScopedIndent bc(this);
+ {
+ line() << "type: ";
+ ScopedIndent ty(this);
+ if (!EmitExpression(expr->type)) {
+ return false;
+ }
+ }
+ {
+ line() << "expr: ";
+ ScopedIndent exp(this);
+ if (!EmitExpression(expr->expr)) {
+ return false;
+ }
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitCall(const ast::CallExpression* expr) {
+ line() << "Call [";
+ {
+ ScopedIndent cl(this);
+
+ line() << "target: [";
+ {
+ ScopedIndent tgt(this);
+ if (!EmitExpression(expr->target)) {
+ return false;
+ }
+ }
+ line() << "]";
+
+ if (!expr->args.IsEmpty()) {
+ line() << "args: [";
+ {
+ ScopedIndent args(this);
+ for (auto* arg : expr->args) {
+ line() << "arg: [";
+ {
+ ScopedIndent arg_val(this);
+ if (!EmitExpression(arg)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitLiteral(const ast::LiteralExpression* lit) {
+ bool ret = false;
+ line() << "LiteralExpression [";
+ {
+ ScopedIndent le(this);
+ ret = Switch(
+ lit,
+ [&](const ast::BoolLiteralExpression* l) { //
+ line() << (l->value ? "true" : "false");
+ return true;
+ },
+ [&](const ast::FloatLiteralExpression* l) { //
+ // f16 literals are also emitted as float value with suffix "h".
+ // Note that all normal and subnormal f16 values are normal f32 values, and since
+ // NaN and Inf are not allowed to be spelled in literal, it should be fine to emit
+ // f16 literals in this way.
+ if (l->suffix == ast::FloatLiteralExpression::Suffix::kNone) {
+ line() << DoubleToBitPreservingString(l->value);
+ } else {
+ line() << FloatToBitPreservingString(static_cast<float>(l->value)) << l->suffix;
+ }
+ return true;
+ },
+ [&](const ast::IntLiteralExpression* l) { //
+ line() << l->value << l->suffix;
+ return true;
+ },
+ [&](Default) { //
+ diagnostics_.add_error(diag::System::Writer, "unknown literal type");
+ return false;
+ });
+ }
+ line() << "]";
+ return ret;
+}
+
+bool GeneratorImpl::EmitIdentifier(const ast::IdentifierExpression* expr) {
+ bool ret = false;
+ line() << "IdentifierExpression [";
+ {
+ ScopedIndent ie(this);
+ ret = EmitIdentifier(expr->identifier);
+ }
+ line() << "]";
+ return ret;
+}
+
+bool GeneratorImpl::EmitIdentifier(const ast::Identifier* ident) {
+ line() << "Identifier [";
+ {
+ ScopedIndent id(this);
+ if (auto* tmpl_ident = ident->As<ast::TemplatedIdentifier>()) {
+ line() << "Templated [";
+ {
+ ScopedIndent tmpl(this);
+ if (!tmpl_ident->attributes.IsEmpty()) {
+ line() << "attrs: [";
+ {
+ ScopedIndent attrs(this);
+ EmitAttributes(tmpl_ident->attributes);
+ }
+ line() << "]";
+ }
+ line() << "name: " << program_->Symbols().NameFor(ident->symbol);
+ if (!tmpl_ident->arguments.IsEmpty()) {
+ line() << "args: [";
+ {
+ ScopedIndent args(this);
+ for (auto* expr : tmpl_ident->arguments) {
+ if (!EmitExpression(expr)) {
+ return false;
+ }
+ }
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+ } else {
+ line() << program_->Symbols().NameFor(ident->symbol);
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitFunction(const ast::Function* func) {
+ line() << "Function [";
+ {
+ ScopedIndent funct(this);
+
+ if (func->attributes.Length()) {
+ line() << "attrs: [";
+ {
+ ScopedIndent attrs(this);
+ if (!EmitAttributes(func->attributes)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ line() << "name: " << program_->Symbols().NameFor(func->name->symbol);
+
+ if (!func->params.IsEmpty()) {
+ line() << "params: [";
+ {
+ ScopedIndent args(this);
+ for (auto* v : func->params) {
+ line() << "param: [";
+ {
+ ScopedIndent param(this);
+ line() << "name: " << program_->Symbols().NameFor(v->name->symbol);
+ if (!v->attributes.IsEmpty()) {
+ line() << "attrs: [";
+ {
+ ScopedIndent attrs(this);
+ if (!EmitAttributes(v->attributes)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ line() << "type: [";
+ {
+ ScopedIndent ty(this);
+ if (!EmitExpression(v->type)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+ }
+
+ line() << "return: [";
+ {
+ ScopedIndent ret(this);
+
+ if (func->return_type || !func->return_type_attributes.IsEmpty()) {
+ if (!func->return_type_attributes.IsEmpty()) {
+ line() << "attrs: [";
+ {
+ ScopedIndent attrs(this);
+ if (!EmitAttributes(func->return_type_attributes)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+
+ line() << "type: [";
+ {
+ ScopedIndent ty(this);
+ if (!EmitExpression(func->return_type)) {
+ return false;
+ }
+ }
+ line() << "]";
+ } else {
+ line() << "void";
+ }
+ }
+ line() << "]";
+ line() << "body: [";
+ {
+ ScopedIndent bdy(this);
+ if (func->body) {
+ if (!EmitBlockHeader(func->body)) {
+ return false;
+ }
+ if (!EmitStatementsWithIndent(func->body->statements)) {
+ return false;
+ }
+ }
+ }
+ line() << "]";
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitImageFormat(const builtin::TexelFormat fmt) {
+ line() << "builtin::TexelFormat [" << fmt << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitStructType(const ast::Struct* str) {
+ line() << "Struct [";
+ {
+ ScopedIndent strct(this);
+
+ if (str->attributes.Length()) {
+ line() << "attrs: [";
+ {
+ ScopedIndent attrs(this);
+ if (!EmitAttributes(str->attributes)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ line() << "name: " << program_->Symbols().NameFor(str->name->symbol);
+ line() << "members: [";
+ {
+ ScopedIndent membs(this);
+
+ for (auto* mem : str->members) {
+ line() << "StructMember[";
+ {
+ ScopedIndent m(this);
+ if (!mem->attributes.IsEmpty()) {
+ line() << "attrs: [";
+ {
+ ScopedIndent attrs(this);
+ if (!EmitAttributes(mem->attributes)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+
+ line() << "name: " << program_->Symbols().NameFor(mem->name->symbol);
+ line() << "type: [";
+ {
+ ScopedIndent ty(this);
+ if (!EmitExpression(mem->type)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+ }
+ line() << "]";
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitVariable(const ast::Variable* v) {
+ line() << "Variable [";
+ {
+ ScopedIndent variable(this);
+ if (!v->attributes.IsEmpty()) {
+ line() << "attrs: [";
+ {
+ ScopedIndent attr(this);
+ if (!EmitAttributes(v->attributes)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+
+ bool ok = Switch(
+ v, //
+ [&](const ast::Var* var) {
+ if (var->declared_address_space || var->declared_access) {
+ line() << "Var [";
+ {
+ ScopedIndent vr(this);
+ line() << "address_space: [";
+ {
+ ScopedIndent addr(this);
+ if (!EmitExpression(var->declared_address_space)) {
+ return false;
+ }
+ }
+ line() << "]";
+ if (var->declared_access) {
+ line() << "access: [";
+ {
+ ScopedIndent acs(this);
+ if (!EmitExpression(var->declared_access)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+ } else {
+ line() << "Var []";
+ }
+ return true;
+ },
+ [&](const ast::Let*) {
+ line() << "Let []";
+ return true;
+ },
+ [&](const ast::Override*) {
+ line() << "Override []";
+ return true;
+ },
+ [&](const ast::Const*) {
+ line() << "Const []";
+ return true;
+ },
+ [&](Default) {
+ TINT_ICE(Writer, diagnostics_) << "unhandled variable type " << v->TypeInfo().name;
+ return false;
+ });
+ if (!ok) {
+ return false;
+ }
+
+ line() << "name: " << program_->Symbols().NameFor(v->name->symbol);
+
+ if (auto ty = v->type) {
+ line() << "type: [";
+ {
+ ScopedIndent vty(this);
+ if (!EmitExpression(ty)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+
+ if (v->initializer != nullptr) {
+ line() << "initializer: [";
+ {
+ ScopedIndent init(this);
+ if (!EmitExpression(v->initializer)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs) {
+ for (auto* attr : attrs) {
+ bool ok = Switch(
+ attr,
+ [&](const ast::WorkgroupAttribute* workgroup) {
+ auto values = workgroup->Values();
+ line() << "WorkgroupAttribute [";
+ {
+ ScopedIndent wg(this);
+ for (size_t i = 0; i < 3; i++) {
+ if (values[i]) {
+ if (!EmitExpression(values[i])) {
+ return false;
+ }
+ }
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::StageAttribute* stage) {
+ line() << "StageAttribute [" << stage->stage << "]";
+ return true;
+ },
+ [&](const ast::BindingAttribute* binding) {
+ line() << "BindingAttribute [";
+ {
+ ScopedIndent ba(this);
+ if (!EmitExpression(binding->expr)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::GroupAttribute* group) {
+ line() << "GroupAttribute [";
+ {
+ ScopedIndent ga(this);
+ if (!EmitExpression(group->expr)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::LocationAttribute* location) {
+ line() << "LocationAttribute [";
+ {
+ ScopedIndent la(this);
+ if (!EmitExpression(location->expr)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::BuiltinAttribute* builtin) {
+ line() << "BuiltinAttribute [";
+ {
+ ScopedIndent ba(this);
+ if (!EmitExpression(builtin->builtin)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::DiagnosticAttribute* diagnostic) {
+ return EmitDiagnosticControl(diagnostic->control);
+ },
+ [&](const ast::InterpolateAttribute* interpolate) {
+ line() << "InterpolateAttribute [";
+ {
+ ScopedIndent ia(this);
+ line() << "type: [";
+ {
+ ScopedIndent ty(this);
+ if (!EmitExpression(interpolate->type)) {
+ return false;
+ }
+ }
+ line() << "]";
+ if (interpolate->sampling) {
+ line() << "sampling: [";
+ {
+ ScopedIndent sa(this);
+ if (!EmitExpression(interpolate->sampling)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::InvariantAttribute*) {
+ line() << "InvariantAttribute []";
+ return true;
+ },
+ [&](const ast::IdAttribute* override_deco) {
+ line() << "IdAttribute [";
+ {
+ ScopedIndent id(this);
+ if (!EmitExpression(override_deco->expr)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::MustUseAttribute*) {
+ line() << "MustUseAttribute []";
+ return true;
+ },
+ [&](const ast::StructMemberOffsetAttribute* offset) {
+ line() << "StructMemberOffsetAttribute [";
+ {
+ ScopedIndent smoa(this);
+ if (!EmitExpression(offset->expr)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::StructMemberSizeAttribute* size) {
+ line() << "StructMemberSizeAttribute [";
+ {
+ ScopedIndent smsa(this);
+ if (!EmitExpression(size->expr)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::StructMemberAlignAttribute* align) {
+ line() << "StructMemberAlignAttribute [";
+ {
+ ScopedIndent smaa(this);
+ if (!EmitExpression(align->expr)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+ },
+ [&](const ast::StrideAttribute* stride) {
+ line() << "StrideAttribute [" << stride->stride << "]";
+ return true;
+ },
+ [&](const ast::InternalAttribute* internal) {
+ line() << "InternalAttribute [" << internal->InternalName() << "]";
+ return true;
+ },
+ [&](Default) {
+ TINT_ICE(Writer, diagnostics_)
+ << "Unsupported attribute '" << attr->TypeInfo().name << "'";
+ return false;
+ });
+
+ if (!ok) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GeneratorImpl::EmitBinary(const ast::BinaryExpression* expr) {
+ line() << "BinaryExpression [";
+ {
+ ScopedIndent be(this);
+ line() << "lhs: [";
+ {
+ ScopedIndent lhs(this);
+
+ if (!EmitExpression(expr->lhs)) {
+ return false;
+ }
+ }
+ line() << "]";
+ line() << "op: [";
+ {
+ ScopedIndent op(this);
+ if (!EmitBinaryOp(expr->op)) {
+ return false;
+ }
+ }
+ line() << "]";
+ line() << "rhs: [";
+ {
+ ScopedIndent rhs(this);
+ if (!EmitExpression(expr->rhs)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitBinaryOp(const ast::BinaryOp op) {
+ switch (op) {
+ case ast::BinaryOp::kAnd:
+ line() << "&";
+ break;
+ case ast::BinaryOp::kOr:
+ line() << "|";
+ break;
+ case ast::BinaryOp::kXor:
+ line() << "^";
+ break;
+ case ast::BinaryOp::kLogicalAnd:
+ line() << "&&";
+ break;
+ case ast::BinaryOp::kLogicalOr:
+ line() << "||";
+ break;
+ case ast::BinaryOp::kEqual:
+ line() << "==";
+ break;
+ case ast::BinaryOp::kNotEqual:
+ line() << "!=";
+ break;
+ case ast::BinaryOp::kLessThan:
+ line() << "<";
+ break;
+ case ast::BinaryOp::kGreaterThan:
+ line() << ">";
+ break;
+ case ast::BinaryOp::kLessThanEqual:
+ line() << "<=";
+ break;
+ case ast::BinaryOp::kGreaterThanEqual:
+ line() << ">=";
+ break;
+ case ast::BinaryOp::kShiftLeft:
+ line() << "<<";
+ break;
+ case ast::BinaryOp::kShiftRight:
+ line() << ">>";
+ break;
+ case ast::BinaryOp::kAdd:
+ line() << "+";
+ break;
+ case ast::BinaryOp::kSubtract:
+ line() << "-";
+ break;
+ case ast::BinaryOp::kMultiply:
+ line() << "*";
+ break;
+ case ast::BinaryOp::kDivide:
+ line() << "/";
+ break;
+ case ast::BinaryOp::kModulo:
+ line() << "%";
+ break;
+ case ast::BinaryOp::kNone:
+ diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
+ return false;
+ }
+ return true;
+}
+
+bool GeneratorImpl::EmitUnaryOp(const ast::UnaryOpExpression* expr) {
+ line() << "UnaryOpExpression [";
+ {
+ ScopedIndent uoe(this);
+ line() << "op: [";
+ {
+ ScopedIndent op(this);
+ switch (expr->op) {
+ case ast::UnaryOp::kAddressOf:
+ line() << "&";
+ break;
+ case ast::UnaryOp::kComplement:
+ line() << "~";
+ break;
+ case ast::UnaryOp::kIndirection:
+ line() << "*";
+ break;
+ case ast::UnaryOp::kNot:
+ line() << "!";
+ break;
+ case ast::UnaryOp::kNegation:
+ line() << "-";
+ break;
+ }
+ }
+ line() << "]";
+ line() << "expr: [";
+ {
+ ScopedIndent ex(this);
+ if (!EmitExpression(expr->expr)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ line() << "]";
+
+ return true;
+}
+
+bool GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
+ {
+ if (!EmitBlockHeader(stmt)) {
+ return false;
+ }
+ }
+ if (!EmitStatementsWithIndent(stmt->statements)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool GeneratorImpl::EmitBlockHeader(const ast::BlockStatement* stmt) {
+ if (!stmt->attributes.IsEmpty()) {
+ line() << "attrs: [";
+ {
+ ScopedIndent attrs(this);
+ if (!EmitAttributes(stmt->attributes)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ return true;
+}
+
+bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
+ return Switch(
+ stmt, //
+ [&](const ast::AssignmentStatement* a) { return EmitAssign(a); },
+ [&](const ast::BlockStatement* b) { return EmitBlock(b); },
+ [&](const ast::BreakStatement* b) { return EmitBreak(b); },
+ [&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
+ [&](const ast::CallStatement* c) { return EmitCall(c->expr); },
+ [&](const ast::CompoundAssignmentStatement* c) { return EmitCompoundAssign(c); },
+ [&](const ast::ContinueStatement* c) { return EmitContinue(c); },
+ [&](const ast::DiscardStatement* d) { return EmitDiscard(d); },
+ [&](const ast::IfStatement* i) { return EmitIf(i); },
+ [&](const ast::IncrementDecrementStatement* l) { return EmitIncrementDecrement(l); },
+ [&](const ast::LoopStatement* l) { return EmitLoop(l); },
+ [&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
+ [&](const ast::WhileStatement* l) { return EmitWhile(l); },
+ [&](const ast::ReturnStatement* r) { return EmitReturn(r); },
+ [&](const ast::ConstAssert* c) { return EmitConstAssert(c); },
+ [&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
+ [&](const ast::VariableDeclStatement* v) { return EmitVariable(v->variable); },
+ [&](Default) {
+ diagnostics_.add_error(diag::System::Writer,
+ "unknown statement type: " + std::string(stmt->TypeInfo().name));
+ return false;
+ });
+}
+
+bool GeneratorImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts) {
+ for (auto* s : stmts) {
+ if (!EmitStatement(s)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool GeneratorImpl::EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts) {
+ ScopedIndent si(this);
+ return EmitStatements(stmts);
+}
+
+bool GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) {
+ line() << "AssignmentStatement [";
+ {
+ ScopedIndent as(this);
+ line() << "lhs: [";
+ {
+ ScopedIndent lhs(this);
+ if (!EmitExpression(stmt->lhs)) {
+ return false;
+ }
+ }
+ line() << "]";
+ line() << "rhs: [";
+ {
+ ScopedIndent rhs(this);
+ if (!EmitExpression(stmt->rhs)) {
+ return false;
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+
+ return true;
+}
+
+bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
+ line() << "BreakStatement []";
+ return true;
+}
+
+bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
+ line() << "BreakIfStatement [";
+ {
+ ScopedIndent bis(this);
+ if (!EmitExpression(b->condition)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
+ line() << "CaseStatement [";
+ {
+ ScopedIndent cs(this);
+ if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) {
+ line() << "selector: default";
+ if (!EmitBlockHeader(stmt->body)) {
+ return false;
+ }
+ } else {
+ line() << "selectors: [";
+ {
+ ScopedIndent sels(this);
+ for (auto* sel : stmt->selectors) {
+ if (sel->IsDefault()) {
+ line() << "default []";
+ } else if (!EmitExpression(sel->expr)) {
+ return false;
+ }
+ }
+ }
+ line() << "]";
+ if (!EmitBlockHeader(stmt->body)) {
+ return false;
+ }
+ }
+ if (!EmitStatementsWithIndent(stmt->body->statements)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt) {
+ line() << "CompoundAssignmentStatement [";
+ {
+ ScopedIndent cas(this);
+ line() << "lhs: [";
+ {
+ ScopedIndent lhs(this);
+ if (!EmitExpression(stmt->lhs)) {
+ return false;
+ }
+ }
+ line() << "]";
+
+ line() << "op: [";
+ {
+ ScopedIndent op(this);
+ if (!EmitBinaryOp(stmt->op)) {
+ return false;
+ }
+ }
+ line() << "]";
+ line() << "rhs: [";
+ {
+ ScopedIndent rhs(this);
+
+ if (!EmitExpression(stmt->rhs)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitContinue(const ast::ContinueStatement*) {
+ line() << "ContinueStatement []";
+ return true;
+}
+
+bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
+ {
+ line() << "IfStatement [";
+ {
+ ScopedIndent ifs(this);
+ line() << "condition: [";
+ {
+ ScopedIndent cond(this);
+ if (!EmitExpression(stmt->condition)) {
+ return false;
+ }
+ }
+ line() << "]";
+ if (!EmitBlockHeader(stmt->body)) {
+ return false;
+ }
+ }
+ line() << "] ";
+ }
+ if (!EmitStatementsWithIndent(stmt->body->statements)) {
+ return false;
+ }
+
+ const ast::Statement* e = stmt->else_statement;
+ while (e) {
+ if (auto* elseif = e->As<ast::IfStatement>()) {
+ {
+ line() << "Else IfStatement [";
+ {
+ ScopedIndent ifs(this);
+ line() << "condition: [";
+ if (!EmitExpression(elseif->condition)) {
+ return false;
+ }
+ }
+ line() << "]";
+ if (!EmitBlockHeader(elseif->body)) {
+ return false;
+ }
+ }
+ line() << "]";
+ if (!EmitStatementsWithIndent(elseif->body->statements)) {
+ return false;
+ }
+ e = elseif->else_statement;
+ } else {
+ auto* body = e->As<ast::BlockStatement>();
+ {
+ line() << "Else [";
+ {
+ ScopedIndent els(this);
+ if (!EmitBlockHeader(body)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ if (!EmitStatementsWithIndent(body->statements)) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+bool GeneratorImpl::EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt) {
+ line() << "IncrementDecrementStatement [";
+ {
+ ScopedIndent ids(this);
+ line() << "expr: [";
+ if (!EmitExpression(stmt->lhs)) {
+ return false;
+ }
+ line() << "]";
+ line() << "dir: " << (stmt->increment ? "++" : "--");
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) {
+ line() << "DiscardStatement []";
+ return true;
+}
+
+bool GeneratorImpl::EmitLoop(const ast::LoopStatement* stmt) {
+ line() << "LoopStatement [";
+ {
+ ScopedIndent ls(this);
+ if (!EmitStatements(stmt->body->statements)) {
+ return false;
+ }
+
+ if (stmt->continuing && !stmt->continuing->Empty()) {
+ line() << "Continuing [";
+ {
+ ScopedIndent cont(this);
+ if (!EmitStatementsWithIndent(stmt->continuing->statements)) {
+ return false;
+ }
+ }
+ line() << "]";
+ }
+ }
+ line() << "]";
+
+ return true;
+}
+
+bool GeneratorImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
+ TextBuffer init_buf;
+ if (auto* init = stmt->initializer) {
+ TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
+ if (!EmitStatement(init)) {
+ return false;
+ }
+ }
+
+ TextBuffer cont_buf;
+ if (auto* cont = stmt->continuing) {
+ TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
+ if (!EmitStatement(cont)) {
+ return false;
+ }
+ }
+
+ line() << "ForLoopStatement [";
+ {
+ ScopedIndent fs(this);
+
+ line() << "initializer: [";
+ {
+ ScopedIndent init(this);
+ switch (init_buf.lines.size()) {
+ case 0: // No initializer
+ break;
+ case 1: // Single line initializer statement
+ line() << TrimSuffix(init_buf.lines[0].content, ";");
+ break;
+ default: // Block initializer statement
+ for (size_t i = 1; i < init_buf.lines.size(); i++) {
+ // Indent all by the first line
+ init_buf.lines[i].indent += current_buffer_->current_indent;
+ }
+ line() << TrimSuffix(init_buf.String(), "\n");
+ break;
+ }
+ }
+ line() << "]";
+ line() << "condition: [";
+ {
+ ScopedIndent con(this);
+ if (auto* cond = stmt->condition) {
+ if (!EmitExpression(cond)) {
+ return false;
+ }
+ }
+ }
+
+ line() << "]";
+ line() << "continuing: [";
+ {
+ ScopedIndent cont(this);
+ switch (cont_buf.lines.size()) {
+ case 0: // No continuing
+ break;
+ case 1: // Single line continuing statement
+ line() << TrimSuffix(cont_buf.lines[0].content, ";");
+ break;
+ default: // Block continuing statement
+ for (size_t i = 1; i < cont_buf.lines.size(); i++) {
+ // Indent all by the first line
+ cont_buf.lines[i].indent += current_buffer_->current_indent;
+ }
+ line() << TrimSuffix(cont_buf.String(), "\n");
+ break;
+ }
+ }
+ if (!EmitBlockHeader(stmt->body)) {
+ return false;
+ }
+
+ if (!EmitStatementsWithIndent(stmt->body->statements)) {
+ return false;
+ }
+ }
+ line() << "]";
+
+ return true;
+}
+
+bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
+ line() << "WhileStatement [";
+ {
+ ScopedIndent ws(this);
+ {
+ auto* cond = stmt->condition;
+ if (!EmitExpression(cond)) {
+ return false;
+ }
+ }
+ if (!EmitBlockHeader(stmt->body)) {
+ return false;
+ }
+ if (!EmitStatementsWithIndent(stmt->body->statements)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
+ line() << "ReturnStatement [";
+ {
+ ScopedIndent ret(this);
+ if (stmt->value) {
+ if (!EmitExpression(stmt->value)) {
+ return false;
+ }
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitConstAssert(const ast::ConstAssert* stmt) {
+ line() << "ConstAssert [";
+ {
+ ScopedIndent ca(this);
+ if (!EmitExpression(stmt->condition)) {
+ return false;
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+bool GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
+ line() << "SwitchStatement [";
+ {
+ ScopedIndent ss(this);
+ line() << "condition: [";
+ {
+ ScopedIndent cond(this);
+ if (!EmitExpression(stmt->condition)) {
+ return false;
+ }
+ }
+ line() << "]";
+
+ {
+ ScopedIndent si(this);
+ for (auto* s : stmt->body) {
+ if (!EmitCase(s)) {
+ return false;
+ }
+ }
+ }
+ }
+ line() << "]";
+ return true;
+}
+
+} // namespace tint::writer::syntax_tree
diff --git a/src/tint/writer/syntax_tree/generator_impl.h b/src/tint/writer/syntax_tree/generator_impl.h
new file mode 100644
index 0000000..8b9a187
--- /dev/null
+++ b/src/tint/writer/syntax_tree/generator_impl.h
@@ -0,0 +1,215 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_WRITER_SYNTAX_TREE_GENERATOR_IMPL_H_
+#define SRC_TINT_WRITER_SYNTAX_TREE_GENERATOR_IMPL_H_
+
+#include <string>
+
+#include "src/tint/ast/assignment_statement.h"
+#include "src/tint/ast/binary_expression.h"
+#include "src/tint/ast/bitcast_expression.h"
+#include "src/tint/ast/break_if_statement.h"
+#include "src/tint/ast/break_statement.h"
+#include "src/tint/ast/compound_assignment_statement.h"
+#include "src/tint/ast/continue_statement.h"
+#include "src/tint/ast/discard_statement.h"
+#include "src/tint/ast/for_loop_statement.h"
+#include "src/tint/ast/if_statement.h"
+#include "src/tint/ast/index_accessor_expression.h"
+#include "src/tint/ast/loop_statement.h"
+#include "src/tint/ast/member_accessor_expression.h"
+#include "src/tint/ast/return_statement.h"
+#include "src/tint/ast/switch_statement.h"
+#include "src/tint/ast/unary_op_expression.h"
+#include "src/tint/program.h"
+#include "src/tint/sem/struct.h"
+#include "src/tint/utils/string_stream.h"
+#include "src/tint/writer/text_generator.h"
+
+namespace tint::writer::syntax_tree {
+
+/// Implementation class for AST generator
+class GeneratorImpl : public TextGenerator {
+ public:
+ /// Constructor
+ /// @param program the program
+ explicit GeneratorImpl(const Program* program);
+ ~GeneratorImpl();
+
+ /// Generates the result data
+ /// @returns true on successful generation; false otherwise
+ bool Generate();
+
+ /// Handles generating a diagnostic control
+ /// @param diagnostic the diagnostic control node
+ /// @returns true if the diagnostic control was emitted
+ bool EmitDiagnosticControl(const ast::DiagnosticControl& diagnostic);
+ /// Handles generating an enable directive
+ /// @param enable the enable node
+ /// @returns true if the enable directive was emitted
+ bool EmitEnable(const ast::Enable* enable);
+ /// Handles generating a declared type
+ /// @param ty the declared type to generate
+ /// @returns true if the declared type was emitted
+ bool EmitTypeDecl(const ast::TypeDecl* ty);
+ /// Handles an index accessor expression
+ /// @param expr the expression to emit
+ /// @returns true if the index accessor was emitted
+ bool EmitIndexAccessor(const ast::IndexAccessorExpression* expr);
+ /// Handles an assignment statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted successfully
+ bool EmitAssign(const ast::AssignmentStatement* stmt);
+ /// Handles generating a binary expression
+ /// @param expr the binary expression
+ /// @returns true if the expression was emitted, false otherwise
+ bool EmitBinary(const ast::BinaryExpression* expr);
+ /// Handles generating a binary operator
+ /// @param op the binary operator
+ /// @returns true if the operator was emitted, false otherwise
+ bool EmitBinaryOp(const ast::BinaryOp op);
+ /// Handles generating a bitcast expression
+ /// @param expr the bitcast expression
+ /// @returns true if the bitcast was emitted
+ bool EmitBitcast(const ast::BitcastExpression* expr);
+ /// Handles a block statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted successfully
+ bool EmitBlock(const ast::BlockStatement* stmt);
+ /// Handles emitting the start of a block statement (including attributes)
+ /// @param stmt the block statement to emit the header for
+ /// @returns true if the statement was emitted successfully
+ bool EmitBlockHeader(const ast::BlockStatement* stmt);
+ /// Handles a break statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted successfully
+ bool EmitBreak(const ast::BreakStatement* stmt);
+ /// Handles a break-if statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted successfully
+ bool EmitBreakIf(const ast::BreakIfStatement* stmt);
+ /// Handles generating a call expression
+ /// @param expr the call expression
+ /// @returns true if the call expression is emitted
+ bool EmitCall(const ast::CallExpression* expr);
+ /// Handles a case statement
+ /// @param stmt the statement
+ /// @returns true if the statment was emitted successfully
+ bool EmitCase(const ast::CaseStatement* stmt);
+ /// Handles a compound assignment statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted successfully
+ bool EmitCompoundAssign(const ast::CompoundAssignmentStatement* stmt);
+ /// Handles generating a literal expression
+ /// @param expr the literal expression expression
+ /// @returns true if the literal expression is emitted
+ bool EmitLiteral(const ast::LiteralExpression* expr);
+ /// Handles a continue statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted successfully
+ bool EmitContinue(const ast::ContinueStatement* stmt);
+ /// Handles generate an Expression
+ /// @param expr the expression
+ /// @returns true if the expression was emitted
+ bool EmitExpression(const ast::Expression* expr);
+ /// Handles generating a function
+ /// @param func the function to generate
+ /// @returns true if the function was emitted
+ bool EmitFunction(const ast::Function* func);
+ /// Handles generating an identifier expression
+ /// @param expr the identifier expression
+ /// @returns true if the identifier was emitted
+ bool EmitIdentifier(const ast::IdentifierExpression* expr);
+ /// Handles generating an identifier
+ /// @param ident the identifier
+ /// @returns true if the identifier was emitted
+ bool EmitIdentifier(const ast::Identifier* ident);
+ /// Handles an if statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was successfully emitted
+ bool EmitIf(const ast::IfStatement* stmt);
+ /// Handles an increment/decrement statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was successfully emitted
+ bool EmitIncrementDecrement(const ast::IncrementDecrementStatement* stmt);
+ /// Handles generating a discard statement
+ /// @param stmt the discard statement
+ /// @returns true if the statement was successfully emitted
+ bool EmitDiscard(const ast::DiscardStatement* stmt);
+ /// Handles a loop statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emtited
+ bool EmitLoop(const ast::LoopStatement* stmt);
+ /// Handles a for-loop statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emtited
+ bool EmitForLoop(const ast::ForLoopStatement* stmt);
+ /// Handles a while statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emtited
+ bool EmitWhile(const ast::WhileStatement* stmt);
+ /// Handles a member accessor expression
+ /// @param expr the member accessor expression
+ /// @returns true if the member accessor was emitted
+ bool EmitMemberAccessor(const ast::MemberAccessorExpression* expr);
+ /// Handles return statements
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was successfully emitted
+ bool EmitReturn(const ast::ReturnStatement* stmt);
+ /// Handles const assertion statements
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was successfully emitted
+ bool EmitConstAssert(const ast::ConstAssert* stmt);
+ /// Handles statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted
+ bool EmitStatement(const ast::Statement* stmt);
+ /// Handles a statement list
+ /// @param stmts the statements to emit
+ /// @returns true if the statements were emitted
+ bool EmitStatements(utils::VectorRef<const ast::Statement*> stmts);
+ /// Handles a statement list with an increased indentation
+ /// @param stmts the statements to emit
+ /// @returns true if the statements were emitted
+ bool EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts);
+ /// Handles generating a switch statement
+ /// @param stmt the statement to emit
+ /// @returns true if the statement was emitted
+ bool EmitSwitch(const ast::SwitchStatement* stmt);
+ /// Handles generating a struct declaration
+ /// @param str the struct
+ /// @returns true if the struct is emitted
+ bool EmitStructType(const ast::Struct* str);
+ /// Handles emitting an image format
+ /// @param fmt the format to generate
+ /// @returns true if the format is emitted
+ bool EmitImageFormat(const builtin::TexelFormat fmt);
+ /// Handles a unary op expression
+ /// @param expr the expression to emit
+ /// @returns true if the expression was emitted
+ bool EmitUnaryOp(const ast::UnaryOpExpression* expr);
+ /// Handles generating a variable
+ /// @param var the variable to generate
+ /// @returns true if the variable was emitted
+ bool EmitVariable(const ast::Variable* var);
+ /// Handles generating a attribute list
+ /// @param attrs the attribute list
+ /// @returns true if the attributes were emitted
+ bool EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs);
+};
+
+} // namespace tint::writer::syntax_tree
+
+#endif // SRC_TINT_WRITER_SYNTAX_TREE_GENERATOR_IMPL_H_
diff --git a/tint_overrides_with_defaults.gni b/tint_overrides_with_defaults.gni
index 9e39248..c54915d 100644
--- a/tint_overrides_with_defaults.gni
+++ b/tint_overrides_with_defaults.gni
@@ -72,6 +72,11 @@
tint_build_glsl_writer = true
}
+ # Build the Syntax Tree writer
+ if (!defined(tint_build_syntax_tree_writer)) {
+ tint_build_syntax_tree_writer = false
+ }
+
# Build unittests
if (!defined(tint_build_unittests)) {
tint_build_unittests = true