Initial commit
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..3d71fc7
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,310 @@
+# Copyright 2020 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.
+
+set(TINT_LIB_SRCS
+ ast/array_accessor_expression.cc
+ ast/array_accessor_expression.h
+ ast/as_expression.cc
+ ast/as_expression.h
+ ast/assignment_statement.cc
+ ast/assignment_statement.h
+ ast/binding_decoration.cc
+ ast/binding_decoration.h
+ ast/bool_literal.h
+ ast/bool_literal.cc
+ ast/break_statement.cc
+ ast/break_statement.h
+ ast/builtin.cc
+ ast/builtin.h
+ ast/builtin_decoration.cc
+ ast/builtin_decoration.h
+ ast/call_expression.cc
+ ast/call_expression.h
+ ast/case_statement.cc
+ ast/case_statement.h
+ ast/cast_expression.cc
+ ast/cast_expression.h
+ ast/const_initializer_expression.cc
+ ast/const_initializer_expression.h
+ ast/continue_statement.cc
+ ast/continue_statement.h
+ ast/decorated_variable.cc
+ ast/decorated_variable.h
+ ast/derivative_modifier.cc
+ ast/derivative_modifier.h
+ ast/else_statement.cc
+ ast/else_statement.h
+ ast/entry_point.cc
+ ast/entry_point.h
+ ast/expression.cc
+ ast/expression.h
+ ast/fallthrough_statement.cc
+ ast/fallthrough_statement.h
+ ast/float_literal.cc
+ ast/float_literal.h
+ ast/function.cc
+ ast/function.h
+ ast/identifier_expression.cc
+ ast/identifier_expression.h
+ ast/if_statement.cc
+ ast/if_statement.h
+ ast/import.cc
+ ast/import.h
+ ast/initializer_expression.cc
+ ast/initializer_expression.h
+ ast/int_literal.cc
+ ast/int_literal.h
+ ast/kill_statement.cc
+ ast/kill_statement.h
+ ast/literal.h
+ ast/literal.cc
+ ast/location_decoration.cc
+ ast/location_decoration.h
+ ast/loop_statement.cc
+ ast/loop_statement.h
+ ast/member_accessor_expression.cc
+ ast/member_accessor_expression.h
+ ast/module.cc
+ ast/module.h
+ ast/node.cc
+ ast/node.h
+ ast/nop_statement.cc
+ ast/nop_statement.h
+ ast/pipeline_stage.cc
+ ast/pipeline_stage.h
+ ast/regardless_statement.cc
+ ast/regardless_statement.h
+ ast/relational_expression.cc
+ ast/relational_expression.h
+ ast/return_statement.cc
+ ast/return_statement.h
+ ast/set_decoration.cc
+ ast/set_decoration.h
+ ast/statement.cc
+ ast/statement.h
+ ast/statement_condition.cc
+ ast/statement_condition.h
+ ast/storage_class.cc
+ ast/storage_class.h
+ ast/struct_decoration.cc
+ ast/struct_decoration.h
+ ast/struct.cc
+ ast/struct.h
+ ast/struct_member.cc
+ ast/struct_member.h
+ ast/struct_member_decoration.cc
+ ast/struct_member_decoration.h
+ ast/struct_member_offset_decoration.cc
+ ast/struct_member_offset_decoration.h
+ ast/switch_statement.cc
+ ast/switch_statement.h
+ ast/type_initializer_expression.h
+ ast/type_initializer_expression.cc
+ ast/type/alias_type.cc
+ ast/type/alias_type.h
+ ast/type/array_type.cc
+ ast/type/array_type.h
+ ast/type/bool_type.cc
+ ast/type/bool_type.h
+ ast/type/f32_type.cc
+ ast/type/f32_type.h
+ ast/type/i32_type.cc
+ ast/type/i32_type.h
+ ast/type/matrix_type.cc
+ ast/type/matrix_type.h
+ ast/type/pointer_type.cc
+ ast/type/pointer_type.h
+ ast/type/struct_type.cc
+ ast/type/struct_type.h
+ ast/type/type.cc
+ ast/type/type.h
+ ast/type/u32_type.cc
+ ast/type/u32_type.h
+ ast/type/vector_type.cc
+ ast/type/vector_type.h
+ ast/type/void_type.cc
+ ast/type/void_type.h
+ ast/uint_literal.cc
+ ast/uint_literal.h
+ ast/unary_derivative.cc
+ ast/unary_derivative.h
+ ast/unary_derivative_expression.cc
+ ast/unary_derivative_expression.h
+ ast/unary_method.cc
+ ast/unary_method.h
+ ast/unary_method_expression.cc
+ ast/unary_method_expression.h
+ ast/unary_op.cc
+ ast/unary_op.h
+ ast/unary_op_expression.cc
+ ast/unary_op_expression.h
+ ast/unless_statement.cc
+ ast/unless_statement.h
+ ast/variable.cc
+ ast/variable.h
+ ast/variable_decoration.cc
+ ast/variable_decoration.h
+ ast/variable_statement.cc
+ ast/variable_statement.h
+ reader/reader.cc
+ reader/reader.h
+ reader/wgsl/lexer.cc
+ reader/wgsl/lexer.h
+ reader/wgsl/parser.cc
+ reader/wgsl/parser.h
+ reader/wgsl/parser_impl.cc
+ reader/wgsl/parser_impl.h
+ reader/wgsl/token.cc
+ reader/wgsl/token.h
+ source.h
+ type_determiner.cc
+ type_determiner.h
+ type_manager.cc
+ type_manager.h
+ validator.cc
+ validator.h
+ # TODO(dsinclair): The writers should all be optional
+ writer/spv/generator.cc
+ writer/spv/generator.h
+ writer/wgsl/generator.cc
+ writer/wgsl/generator.h
+ writer/writer.cc
+ writer/writer.h
+)
+
+if(TINT_BUILD_SPV_PARSER)
+ list(APPEND TINT_LIB_SRCS
+ reader/spv/parser.cc
+ reader/spv/parser.h
+ )
+endif()
+
+set(TINT_TEST_SRCS
+ ast/binding_decoration_test.cc
+ ast/bool_literal_test.cc
+ ast/builtin_decoration_test.cc
+ ast/entry_point_test.cc
+ ast/import_test.cc
+ ast/int_literal_test.cc
+ ast/location_decoration_test.cc
+ ast/module_test.cc
+ ast/set_decoration_test.cc
+ ast/struct_member_test.cc
+ ast/struct_member_offset_decoration_test.cc
+ ast/struct_test.cc
+ ast/type/alias_type_test.cc
+ ast/type/array_type_test.cc
+ ast/type/bool_type_test.cc
+ ast/type/f32_type_test.cc
+ ast/type/i32_type_test.cc
+ ast/type/matrix_type_test.cc
+ ast/type/pointer_type_test.cc
+ ast/type/struct_type_test.cc
+ ast/type/u32_type_test.cc
+ ast/type/vector_type_test.cc
+ ast/uint_literal_test.cc
+ ast/variable_test.cc
+ reader/wgsl/lexer_test.cc
+ reader/wgsl/parser_test.cc
+ reader/wgsl/parser_impl_additive_expression_test.cc
+ reader/wgsl/parser_impl_and_expression_test.cc
+ reader/wgsl/parser_impl_argument_expression_list_test.cc
+ reader/wgsl/parser_impl_assignment_stmt_test.cc
+ reader/wgsl/parser_impl_body_stmt_test.cc
+ reader/wgsl/parser_impl_break_stmt_test.cc
+ reader/wgsl/parser_impl_builtin_decoration_test.cc
+ reader/wgsl/parser_impl_case_body_test.cc
+ reader/wgsl/parser_impl_const_expr_test.cc
+ reader/wgsl/parser_impl_const_literal_test.cc
+ reader/wgsl/parser_impl_continue_stmt_test.cc
+ reader/wgsl/parser_impl_continuing_stmt_test.cc
+ reader/wgsl/parser_impl_derivative_modifier_test.cc
+ reader/wgsl/parser_impl_else_stmt_test.cc
+ reader/wgsl/parser_impl_elseif_stmt_test.cc
+ reader/wgsl/parser_impl_entry_point_decl_test.cc
+ reader/wgsl/parser_impl_equality_expression_test.cc
+ reader/wgsl/parser_impl_exclusive_or_expression_test.cc
+ reader/wgsl/parser_impl_function_decl_test.cc
+ reader/wgsl/parser_impl_function_header_test.cc
+ reader/wgsl/parser_impl_function_type_decl_test.cc
+ reader/wgsl/parser_impl_global_constant_decl_test.cc
+ reader/wgsl/parser_impl_global_decl_test.cc
+ reader/wgsl/parser_impl_global_variable_decl_test.cc
+ reader/wgsl/parser_impl_if_stmt_test.cc
+ reader/wgsl/parser_impl_import_decl_test.cc
+ reader/wgsl/parser_impl_inclusive_or_expression_test.cc
+ reader/wgsl/parser_impl_logical_and_expression_test.cc
+ reader/wgsl/parser_impl_logical_or_expression_test.cc
+ reader/wgsl/parser_impl_loop_stmt_test.cc
+ reader/wgsl/parser_impl_multiplicative_expression_test.cc
+ reader/wgsl/parser_impl_param_list_test.cc
+ reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
+ reader/wgsl/parser_impl_pipeline_stage_test.cc
+ reader/wgsl/parser_impl_postfix_expression_test.cc
+ reader/wgsl/parser_impl_premerge_stmt_test.cc
+ reader/wgsl/parser_impl_primary_expression_test.cc
+ reader/wgsl/parser_impl_regardless_stmt_test.cc
+ reader/wgsl/parser_impl_relational_expression_test.cc
+ reader/wgsl/parser_impl_shift_expression_test.cc
+ reader/wgsl/parser_impl_statement_test.cc
+ reader/wgsl/parser_impl_statements_test.cc
+ reader/wgsl/parser_impl_storage_class_test.cc
+ reader/wgsl/parser_impl_struct_body_decl_test.cc
+ reader/wgsl/parser_impl_struct_decl_test.cc
+ reader/wgsl/parser_impl_struct_decoration_decl_test.cc
+ reader/wgsl/parser_impl_struct_decoration_test.cc
+ reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc
+ reader/wgsl/parser_impl_struct_member_decoration_test.cc
+ reader/wgsl/parser_impl_struct_member_test.cc
+ reader/wgsl/parser_impl_switch_body_test.cc
+ reader/wgsl/parser_impl_switch_stmt_test.cc
+ reader/wgsl/parser_impl_test.cc
+ reader/wgsl/parser_impl_type_alias_test.cc
+ reader/wgsl/parser_impl_type_decl_test.cc
+ reader/wgsl/parser_impl_unary_expression_test.cc
+ reader/wgsl/parser_impl_unless_stmt_test.cc
+ reader/wgsl/parser_impl_variable_decl_test.cc
+ reader/wgsl/parser_impl_variable_decoration_list_test.cc
+ reader/wgsl/parser_impl_variable_decoration_test.cc
+ reader/wgsl/parser_impl_variable_ident_decl_test.cc
+ reader/wgsl/parser_impl_variable_stmt_test.cc
+ reader/wgsl/parser_impl_variable_storage_decoration_test.cc
+ reader/wgsl/token_test.cc
+ type_manager_test.cc
+)
+
+## Tint library
+add_library(libtint ${TINT_LIB_SRCS})
+tint_default_compile_options(libtint)
+set_target_properties(libtint PROPERTIES OUTPUT_NAME "tint")
+
+if(${TINT_ENABLE_SPV_PARSER})
+ target_link_libraries(libtint SPIRV-Tools)
+endif()
+
+add_executable(tint_unittests ${TINT_TEST_SRCS})
+if (NOT MSVC)
+ target_compile_options(tint_unittests PRIVATE
+ -Wno-global-constructors
+ -Wno-weak-vtables
+ )
+endif()
+
+## Test executable
+target_include_directories(
+ tint_unittests PRIVATE ${gmock_SOURCE_DIR}/include)
+target_link_libraries(tint_unittests libtint gmock_main)
+tint_default_compile_options(tint_unittests)
+
+add_test(NAME tint_unittests COMMAND tint_unittests)
diff --git a/src/ast/array_accessor_expression.cc b/src/ast/array_accessor_expression.cc
new file mode 100644
index 0000000..a98b436
--- /dev/null
+++ b/src/ast/array_accessor_expression.cc
@@ -0,0 +1,49 @@
+// Copyright 2020 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/ast/array_accessor_expression.h"
+
+namespace tint {
+namespace ast {
+
+ArrayAccessorExpression::ArrayAccessorExpression(
+ std::unique_ptr<Expression> array,
+ std::unique_ptr<Expression> idx_expr)
+ : Expression(), array_(std::move(array)), idx_expr_(std::move(idx_expr)) {}
+
+ArrayAccessorExpression::ArrayAccessorExpression(
+ const Source& source,
+ std::unique_ptr<Expression> array,
+ std::unique_ptr<Expression> idx_expr)
+ : Expression(source),
+ array_(std::move(array)),
+ idx_expr_(std::move(idx_expr)) {}
+
+ArrayAccessorExpression::~ArrayAccessorExpression() = default;
+
+bool ArrayAccessorExpression::IsValid() const {
+ return array_ != nullptr && idx_expr_ != nullptr;
+}
+
+void ArrayAccessorExpression::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "ArrayAccessor{" << std::endl;
+ array_->to_str(out, indent + 2);
+ idx_expr_->to_str(out, indent + 2);
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/array_accessor_expression.h b/src/ast/array_accessor_expression.h
new file mode 100644
index 0000000..ddffdee
--- /dev/null
+++ b/src/ast/array_accessor_expression.h
@@ -0,0 +1,83 @@
+// Copyright 2020 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_AST_ARRAY_ACCESSOR_EXPRESSION_H_
+#define SRC_AST_ARRAY_ACCESSOR_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// An array accessor expression
+class ArrayAccessorExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param array the array
+ /// @param idx_expr the index expression
+ ArrayAccessorExpression(std::unique_ptr<Expression> array,
+ std::unique_ptr<Expression> idx_expr);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param array the array
+ /// @param idx_expr the index expression
+ ArrayAccessorExpression(const Source& source,
+ std::unique_ptr<Expression> array,
+ std::unique_ptr<Expression> idx_expr);
+ /// Move constructor
+ ArrayAccessorExpression(ArrayAccessorExpression&&) = default;
+ ~ArrayAccessorExpression() override;
+
+ /// Sets the array
+ /// @param array the array
+ void set_array(std::unique_ptr<Expression> array) {
+ array_ = std::move(array);
+ }
+ /// @returns the array
+ Expression* array() const { return array_.get(); }
+
+ /// Sets the index expression
+ /// @param idx_expr the index expression
+ void set_idx_expr(std::unique_ptr<Expression> idx_expr) {
+ idx_expr_ = std::move(idx_expr);
+ }
+ /// @returns the index expression
+ Expression* idx_expr() const { return idx_expr_.get(); }
+
+ /// @returns true if this is an array accessor expression
+ bool IsArrayAccessor() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ ArrayAccessorExpression(const ArrayAccessorExpression&) = delete;
+
+ std::unique_ptr<Expression> array_;
+ std::unique_ptr<Expression> idx_expr_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_ARRAY_ACCESSOR_EXPRESSION_H_
diff --git a/src/ast/as_expression.cc b/src/ast/as_expression.cc
new file mode 100644
index 0000000..deb9564
--- /dev/null
+++ b/src/ast/as_expression.cc
@@ -0,0 +1,41 @@
+// Copyright 2020 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/ast/as_expression.h"
+
+namespace tint {
+namespace ast {
+
+AsExpression::AsExpression(type::Type* type, std::unique_ptr<Expression> expr)
+ : Expression(), type_(type), expr_(std::move(expr)) {}
+
+AsExpression::AsExpression(const Source& source,
+ type::Type* type,
+ std::unique_ptr<Expression> expr)
+ : Expression(source), type_(type), expr_(std::move(expr)) {}
+
+AsExpression::~AsExpression() = default;
+
+bool AsExpression::IsValid() const {
+ return type_ != nullptr && expr_ != nullptr;
+}
+
+void AsExpression::to_str(std::ostream& out, size_t indent) const {
+ out << "as<" << type_->type_name() << ">(";
+ expr_->to_str(out, indent);
+ out << ")";
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/as_expression.h b/src/ast/as_expression.h
new file mode 100644
index 0000000..93036f8
--- /dev/null
+++ b/src/ast/as_expression.h
@@ -0,0 +1,80 @@
+// Copyright 2020 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_AST_AS_EXPRESSION_H_
+#define SRC_AST_AS_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+
+/// An as expression
+class AsExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param type the type
+ /// @param expr the expr
+ AsExpression(type::Type* type, std::unique_ptr<Expression> expr);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param type the type
+ /// @param expr the expr
+ AsExpression(const Source& source,
+ type::Type* type,
+ std::unique_ptr<Expression> expr);
+ /// Move constructor
+ AsExpression(AsExpression&&) = default;
+ ~AsExpression() override;
+
+ /// Sets the type
+ /// @param type the type
+ void set_type(type::Type* type) { type_ = std::move(type); }
+ /// @returns the left side expression
+ type::Type* type() const { return type_; }
+
+ /// Sets the expr
+ /// @param expr the expression
+ void set_expr(std::unique_ptr<Expression> expr) { expr_ = std::move(expr); }
+ /// @returns the expression
+ Expression* expr() const { return expr_.get(); }
+
+ /// @returns true if this is an as expression
+ bool IsAs() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ AsExpression(const AsExpression&) = delete;
+
+ type::Type* type_;
+ std::unique_ptr<Expression> expr_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_AS_EXPRESSION_H_
diff --git a/src/ast/assignment_statement.cc b/src/ast/assignment_statement.cc
new file mode 100644
index 0000000..8fe711b
--- /dev/null
+++ b/src/ast/assignment_statement.cc
@@ -0,0 +1,47 @@
+// Copyright 2020 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/ast/assignment_statement.h"
+
+namespace tint {
+namespace ast {
+
+AssignmentStatement::AssignmentStatement(std::unique_ptr<Expression> lhs,
+ std::unique_ptr<Expression> rhs)
+ : Statement(), lhs_(std::move(lhs)), rhs_(std::move(rhs)) {}
+
+AssignmentStatement::AssignmentStatement(const Source& source,
+ std::unique_ptr<Expression> lhs,
+ std::unique_ptr<Expression> rhs)
+ : Statement(source), lhs_(std::move(lhs)), rhs_(std::move(rhs)) {}
+
+AssignmentStatement::~AssignmentStatement() = default;
+
+bool AssignmentStatement::IsValid() const {
+ return lhs_ != nullptr && rhs_ != nullptr;
+}
+
+void AssignmentStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Assignment{" << std::endl;
+ lhs_->to_str(out, indent + 2);
+ out << std::endl;
+ rhs_->to_str(out, indent + 2);
+ out << std::endl;
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/assignment_statement.h b/src/ast/assignment_statement.h
new file mode 100644
index 0000000..6441874
--- /dev/null
+++ b/src/ast/assignment_statement.h
@@ -0,0 +1,80 @@
+// Copyright 2020 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_AST_ASSIGNMENT_STATEMENT_H_
+#define SRC_AST_ASSIGNMENT_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// An assignment statement
+class AssignmentStatement : public Statement {
+ public:
+ /// Constructor
+ /// @param lhs the left side of the expression
+ /// @param rhs the right side of the expression
+ AssignmentStatement(std::unique_ptr<Expression> lhs,
+ std::unique_ptr<Expression> rhs);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param lhs the left side of the expression
+ /// @param rhs the right side of the expression
+ AssignmentStatement(const Source& source,
+ std::unique_ptr<Expression> lhs,
+ std::unique_ptr<Expression> rhs);
+ /// Move constructor
+ AssignmentStatement(AssignmentStatement&&) = default;
+ ~AssignmentStatement() override;
+
+ /// Sets the left side of the statement
+ /// @param lhs the left side to set
+ void set_lhs(std::unique_ptr<Expression> lhs) { lhs_ = std::move(lhs); }
+ /// @returns the left side expression
+ Expression* lhs() const { return lhs_.get(); }
+
+ /// Sets the right side of the statement
+ /// @param rhs the right side to set
+ void set_rhs(std::unique_ptr<Expression> rhs) { rhs_ = std::move(rhs); }
+ /// @returns the right side expression
+ Expression* rhs() const { return rhs_.get(); }
+
+ /// @returns true if this is an assignment statement
+ bool IsAssign() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ AssignmentStatement(const AssignmentStatement&) = delete;
+
+ std::unique_ptr<Expression> lhs_;
+ std::unique_ptr<Expression> rhs_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_ASSIGNMENT_STATEMENT_H_
diff --git a/src/ast/binding_decoration.cc b/src/ast/binding_decoration.cc
new file mode 100644
index 0000000..4278dd5
--- /dev/null
+++ b/src/ast/binding_decoration.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 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/ast/binding_decoration.h"
+
+namespace tint {
+namespace ast {
+
+BindingDecoration::BindingDecoration(size_t val) : value_(val) {}
+
+BindingDecoration::~BindingDecoration() = default;
+
+void BindingDecoration::to_str(std::ostream& out) const {
+ out << "binding " << value_;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/binding_decoration.h b/src/ast/binding_decoration.h
new file mode 100644
index 0000000..1a7e573
--- /dev/null
+++ b/src/ast/binding_decoration.h
@@ -0,0 +1,50 @@
+// Copyright 2020 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_AST_BINDING_DECORATION_H_
+#define SRC_AST_BINDING_DECORATION_H_
+
+#include <stddef.h>
+
+#include "src/ast/variable_decoration.h"
+
+namespace tint {
+namespace ast {
+
+/// A binding decoration
+class BindingDecoration : public VariableDecoration {
+ public:
+ /// constructor
+ /// @param value the binding value
+ explicit BindingDecoration(size_t value);
+ ~BindingDecoration() override;
+
+ /// @returns true if this is a binding decoration
+ bool IsBinding() const override { return true; }
+
+ /// @returns the binding value
+ size_t value() const { return value_; }
+
+ /// Outputs the decoration to the given stream
+ /// @param out the stream to output too
+ void to_str(std::ostream& out) const override;
+
+ private:
+ size_t value_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_BINDING_DECORATION_H_
diff --git a/src/ast/binding_decoration_test.cc b/src/ast/binding_decoration_test.cc
new file mode 100644
index 0000000..0a6a57b
--- /dev/null
+++ b/src/ast/binding_decoration_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 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/ast/binding_decoration.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using BindingDecorationTest = testing::Test;
+
+TEST_F(BindingDecorationTest, Creation) {
+ BindingDecoration d{2};
+ EXPECT_EQ(2, d.value());
+}
+
+TEST_F(BindingDecorationTest, Is) {
+ BindingDecoration d{2};
+ EXPECT_TRUE(d.IsBinding());
+ EXPECT_FALSE(d.IsBuiltin());
+ EXPECT_FALSE(d.IsLocation());
+ EXPECT_FALSE(d.IsSet());
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/bool_literal.cc b/src/ast/bool_literal.cc
new file mode 100644
index 0000000..e11e8a5
--- /dev/null
+++ b/src/ast/bool_literal.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 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/ast/bool_literal.h"
+
+namespace tint {
+namespace ast {
+
+BoolLiteral::BoolLiteral(bool value) : value_(value) {}
+
+BoolLiteral::~BoolLiteral() = default;
+
+std::string BoolLiteral::to_str() const {
+ return value_ ? "true" : "false";
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/bool_literal.h b/src/ast/bool_literal.h
new file mode 100644
index 0000000..c568f27
--- /dev/null
+++ b/src/ast/bool_literal.h
@@ -0,0 +1,51 @@
+// Copyright 2020 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_AST_BOOL_LITERAL_H_
+#define SRC_AST_BOOL_LITERAL_H_
+
+#include <string>
+
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// A boolean literal
+class BoolLiteral : public Literal {
+ public:
+ /// Constructor
+ /// @param value the bool literals value
+ explicit BoolLiteral(bool value);
+ ~BoolLiteral() override;
+
+ /// @returns true if this is a bool literal
+ bool IsBool() const override { return true; }
+
+ /// @returns true if the bool literal is true
+ bool IsTrue() const { return value_; }
+ /// @returns true if the bool literal is false
+ bool IsFalse() const { return !value_; }
+
+ /// @returns the literal as a string
+ std::string to_str() const override;
+
+ private:
+ bool value_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_BOOL_LITERAL_H_
diff --git a/src/ast/bool_literal_test.cc b/src/ast/bool_literal_test.cc
new file mode 100644
index 0000000..af01dae
--- /dev/null
+++ b/src/ast/bool_literal_test.cc
@@ -0,0 +1,55 @@
+// Copyright 2020 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/ast/bool_literal.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using BoolLiteralTest = testing::Test;
+
+TEST_F(BoolLiteralTest, True) {
+ BoolLiteral b{true};
+ ASSERT_TRUE(b.IsBool());
+ ASSERT_TRUE(b.IsTrue());
+ ASSERT_FALSE(b.IsFalse());
+}
+
+TEST_F(BoolLiteralTest, False) {
+ BoolLiteral b{false};
+ ASSERT_TRUE(b.IsBool());
+ ASSERT_FALSE(b.IsTrue());
+ ASSERT_TRUE(b.IsFalse());
+}
+
+TEST_F(BoolLiteralTest, Is) {
+ BoolLiteral b{false};
+ EXPECT_TRUE(b.IsBool());
+ EXPECT_FALSE(b.IsInt());
+ EXPECT_FALSE(b.IsFloat());
+ EXPECT_FALSE(b.IsUint());
+}
+
+TEST_F(BoolLiteralTest, ToStr) {
+ BoolLiteral t{true};
+ BoolLiteral f{false};
+
+ EXPECT_EQ(t.to_str(), "true");
+ EXPECT_EQ(f.to_str(), "false");
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/break_statement.cc b/src/ast/break_statement.cc
new file mode 100644
index 0000000..a095e7d
--- /dev/null
+++ b/src/ast/break_statement.cc
@@ -0,0 +1,58 @@
+// Copyright 2020 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/ast/break_statement.h"
+
+namespace tint {
+namespace ast {
+
+BreakStatement::BreakStatement(StatementCondition condition,
+ std::unique_ptr<Expression> conditional)
+ : Statement(),
+ condition_(condition),
+ conditional_(std::move(conditional)) {}
+
+BreakStatement::BreakStatement(const Source& source,
+ StatementCondition condition,
+ std::unique_ptr<Expression> conditional)
+ : Statement(source),
+ condition_(condition),
+ conditional_(std::move(conditional)) {}
+
+BreakStatement::~BreakStatement() = default;
+
+bool BreakStatement::IsValid() const {
+ return condition_ == StatementCondition::kNone || conditional_ != nullptr;
+}
+
+void BreakStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Break";
+
+ if (condition_ != StatementCondition::kNone) {
+ out << "{" << std::endl;
+
+ make_indent(out, indent + 2);
+ out << condition_ << std::endl;
+ conditional_->to_str(out, indent + 2);
+
+ make_indent(out, indent);
+ out << "}";
+ }
+
+ out << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/break_statement.h b/src/ast/break_statement.h
new file mode 100644
index 0000000..9795bd3
--- /dev/null
+++ b/src/ast/break_statement.h
@@ -0,0 +1,84 @@
+// Copyright 2020 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_AST_BREAK_STATEMENT_H_
+#define SRC_AST_BREAK_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+#include "src/ast/statement_condition.h"
+
+namespace tint {
+namespace ast {
+
+/// An break statement
+class BreakStatement : public Statement {
+ public:
+ /// Constructor
+ BreakStatement();
+ /// Constructor
+ /// @param condition the condition type
+ /// @param conditional the condition expression
+ BreakStatement(StatementCondition condition,
+ std::unique_ptr<Expression> conditional);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param condition the condition type
+ /// @param conditional the condition expression
+ BreakStatement(const Source& source,
+ StatementCondition condition,
+ std::unique_ptr<Expression> conditional);
+ /// Move constructor
+ BreakStatement(BreakStatement&&) = default;
+ ~BreakStatement() override;
+
+ /// Sets the condition type
+ /// @param condition the condition type
+ void set_condition(StatementCondition condition) { condition_ = condition; }
+ /// @returns the condition type
+ StatementCondition condition() const { return condition_; }
+
+ /// Sets the conditional expression
+ /// @param conditional the conditional expression
+ void set_conditional(std::unique_ptr<Expression> conditional) {
+ conditional_ = std::move(conditional);
+ }
+ /// @returns the conditional expression
+ Expression* conditional() const { return conditional_.get(); }
+
+ /// @returns true if this is an break statement
+ bool IsBreak() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ BreakStatement(const BreakStatement&) = delete;
+
+ StatementCondition condition_ = StatementCondition::kNone;
+ std::unique_ptr<Expression> conditional_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_BREAK_STATEMENT_H_
diff --git a/src/ast/builtin.cc b/src/ast/builtin.cc
new file mode 100644
index 0000000..e579f9c
--- /dev/null
+++ b/src/ast/builtin.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 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/ast/builtin.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, Builtin builtin) {
+ switch (builtin) {
+ case Builtin::kNone: {
+ out << "none";
+ break;
+ }
+ case Builtin::kPosition: {
+ out << "position";
+ break;
+ }
+ case Builtin::kVertexIdx: {
+ out << "vertex_idx";
+ break;
+ }
+ case Builtin::kInstanceIdx: {
+ out << "instance_idx";
+ break;
+ }
+ case Builtin::kFrontFacing: {
+ out << "front_facing";
+ break;
+ }
+ case Builtin::kFragCoord: {
+ out << "frag_coord";
+ break;
+ }
+ case Builtin::kFragDepth: {
+ out << "frag_depth";
+ break;
+ }
+ case Builtin::kNumWorkgroups: {
+ out << "num_workgroups";
+ break;
+ }
+ case Builtin::kWorkgroupSize: {
+ out << "workgroup_size";
+ break;
+ }
+ case Builtin::kLocalInvocationId: {
+ out << "local_invocation_id";
+ break;
+ }
+ case Builtin::kLocalInvocationIdx: {
+ out << "local_invocation_idx";
+ break;
+ }
+ case Builtin::kGlobalInvocationId: {
+ out << "global_invocation_id";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/builtin.h b/src/ast/builtin.h
new file mode 100644
index 0000000..511203a
--- /dev/null
+++ b/src/ast/builtin.h
@@ -0,0 +1,44 @@
+// Copyright 2020 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_AST_BUILTIN_H_
+#define SRC_AST_BUILTIN_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// The builtin identifiers
+enum class Builtin {
+ kNone = -1,
+ kPosition,
+ kVertexIdx,
+ kInstanceIdx,
+ kFrontFacing,
+ kFragCoord,
+ kFragDepth,
+ kNumWorkgroups,
+ kWorkgroupSize,
+ kLocalInvocationId,
+ kLocalInvocationIdx,
+ kGlobalInvocationId
+};
+
+std::ostream& operator<<(std::ostream& out, Builtin builtin);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_BUILTIN_H_
diff --git a/src/ast/builtin_decoration.cc b/src/ast/builtin_decoration.cc
new file mode 100644
index 0000000..c0264af
--- /dev/null
+++ b/src/ast/builtin_decoration.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 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/ast/builtin_decoration.h"
+
+namespace tint {
+namespace ast {
+
+BuiltinDecoration::BuiltinDecoration(Builtin builtin) : builtin_(builtin) {}
+
+BuiltinDecoration::~BuiltinDecoration() = default;
+
+void BuiltinDecoration::to_str(std::ostream& out) const {
+ out << "builtin " << builtin_;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/builtin_decoration.h b/src/ast/builtin_decoration.h
new file mode 100644
index 0000000..5027e12
--- /dev/null
+++ b/src/ast/builtin_decoration.h
@@ -0,0 +1,49 @@
+// Copyright 2020 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_AST_BUILTIN_DECORATION_H_
+#define SRC_AST_BUILTIN_DECORATION_H_
+
+#include "src/ast/builtin.h"
+#include "src/ast/variable_decoration.h"
+
+namespace tint {
+namespace ast {
+
+/// A builtin decoration
+class BuiltinDecoration : public VariableDecoration {
+ public:
+ /// constructor
+ /// @param builtin the builtin value
+ explicit BuiltinDecoration(Builtin builtin);
+ ~BuiltinDecoration() override;
+
+ /// @returns true if this is a builtin decoration
+ bool IsBuiltin() const override { return true; }
+
+ /// @returns the builtin value
+ Builtin value() const { return builtin_; }
+
+ /// Outputs the decoration to the given stream
+ /// @param out the stream to output too
+ void to_str(std::ostream& out) const override;
+
+ private:
+ Builtin builtin_ = Builtin::kNone;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_BUILTIN_DECORATION_H_
diff --git a/src/ast/builtin_decoration_test.cc b/src/ast/builtin_decoration_test.cc
new file mode 100644
index 0000000..288674e
--- /dev/null
+++ b/src/ast/builtin_decoration_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 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/ast/builtin_decoration.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using BuiltinDecorationTest = testing::Test;
+
+TEST_F(BuiltinDecorationTest, Creation) {
+ BuiltinDecoration d{Builtin::kFragDepth};
+ EXPECT_EQ(Builtin::kFragDepth, d.value());
+}
+
+TEST_F(BuiltinDecorationTest, Is) {
+ BuiltinDecoration d{Builtin::kFragDepth};
+ EXPECT_FALSE(d.IsBinding());
+ EXPECT_TRUE(d.IsBuiltin());
+ EXPECT_FALSE(d.IsLocation());
+ EXPECT_FALSE(d.IsSet());
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/call_expression.cc b/src/ast/call_expression.cc
new file mode 100644
index 0000000..4b0d97d
--- /dev/null
+++ b/src/ast/call_expression.cc
@@ -0,0 +1,47 @@
+// Copyright 2020 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/ast/call_expression.h"
+
+namespace tint {
+namespace ast {
+
+CallExpression::CallExpression(std::unique_ptr<Expression> func,
+ std::vector<std::unique_ptr<Expression>> params)
+ : Expression(), func_(std::move(func)), params_(std::move(params)) {}
+
+CallExpression::CallExpression(const Source& source,
+ std::unique_ptr<Expression> func,
+ std::vector<std::unique_ptr<Expression>> params)
+ : Expression(source), func_(std::move(func)), params_(std::move(params)) {}
+
+CallExpression::~CallExpression() = default;
+
+bool CallExpression::IsValid() const {
+ return func_ != nullptr;
+}
+
+void CallExpression::to_str(std::ostream& out, size_t indent) const {
+ func_->to_str(out, indent);
+ make_indent(out, indent + 2);
+ out << "(" << std::endl;
+ for (const auto& param : params_)
+ param->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << ")" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/call_expression.h b/src/ast/call_expression.h
new file mode 100644
index 0000000..ac5178f
--- /dev/null
+++ b/src/ast/call_expression.h
@@ -0,0 +1,84 @@
+// Copyright 2020 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_AST_CALL_EXPRESSION_H_
+#define SRC_AST_CALL_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// A call expression
+class CallExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param func the function
+ /// @param params the parameters
+ CallExpression(std::unique_ptr<Expression> func,
+ std::vector<std::unique_ptr<Expression>> params);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param func the function
+ /// @param params the parameters
+ CallExpression(const Source& source,
+ std::unique_ptr<Expression> func,
+ std::vector<std::unique_ptr<Expression>> params);
+ /// Move constructor
+ CallExpression(CallExpression&&) = default;
+ ~CallExpression() override;
+
+ /// Sets the func
+ /// @param func the func
+ void set_func(std::unique_ptr<Expression> func) { func_ = std::move(func); }
+ /// @returns the func
+ Expression* func() const { return func_.get(); }
+
+ /// Sets the parameters
+ /// @param params the parameters
+ void set_params(std::vector<std::unique_ptr<Expression>> params) {
+ params_ = std::move(params);
+ }
+ /// @returns the parameters
+ const std::vector<std::unique_ptr<Expression>>& params() const {
+ return params_;
+ }
+
+ /// @returns true if this is a call expression
+ bool IsCall() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ CallExpression(const CallExpression&) = delete;
+
+ std::unique_ptr<Expression> func_;
+ std::vector<std::unique_ptr<Expression>> params_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_CALL_EXPRESSION_H_
diff --git a/src/ast/case_statement.cc b/src/ast/case_statement.cc
new file mode 100644
index 0000000..a591224
--- /dev/null
+++ b/src/ast/case_statement.cc
@@ -0,0 +1,56 @@
+// Copyright 2020 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/ast/case_statement.h"
+
+namespace tint {
+namespace ast {
+
+CaseStatement::CaseStatement() : Statement() {}
+
+CaseStatement::CaseStatement(std::unique_ptr<Literal> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(), condition_(std::move(condition)), body_(std::move(body)) {}
+
+CaseStatement::CaseStatement(const Source& source,
+ std::unique_ptr<Literal> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(source),
+ condition_(std::move(condition)),
+ body_(std::move(body)) {}
+
+CaseStatement::~CaseStatement() = default;
+
+bool CaseStatement::IsValid() const {
+ return true;
+}
+
+void CaseStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+
+ if (IsDefault()) {
+ out << "default{" << std::endl;
+ } else {
+ out << "Case " << condition_->to_str() << "{" << std::endl;
+ }
+
+ for (const auto& stmt : body_)
+ stmt->to_str(out, indent + 2);
+
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/case_statement.h b/src/ast/case_statement.h
new file mode 100644
index 0000000..8a3ae09
--- /dev/null
+++ b/src/ast/case_statement.h
@@ -0,0 +1,90 @@
+// Copyright 2020 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_AST_CASE_STATEMENT_H_
+#define SRC_AST_CASE_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/statement.h"
+#include "src/ast/statement_condition.h"
+
+namespace tint {
+namespace ast {
+
+/// A case statement
+class CaseStatement : public Statement {
+ public:
+ /// Constructor
+ CaseStatement();
+ /// Constructor
+ /// @param condition the case condition
+ /// @param body the case body
+ CaseStatement(std::unique_ptr<Literal> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Constructor
+ /// @param source the source information
+ /// @param condition the case condition
+ /// @param body the case body
+ CaseStatement(const Source& source,
+ std::unique_ptr<Literal> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Move constructor
+ CaseStatement(CaseStatement&&) = default;
+ ~CaseStatement() override;
+
+ /// Sets the condition for the case statement
+ /// @param condition the condition to set
+ void set_condition(std::unique_ptr<Literal> condition) {
+ condition_ = std::move(condition);
+ }
+ /// @returns the case condition or nullptr if none set
+ Literal* condition() const { return condition_.get(); }
+ /// @returns true if this is a default statement
+ bool IsDefault() const { return condition_ == nullptr; }
+
+ /// Sets the case body
+ /// @param body the case body
+ void set_body(std::vector<std::unique_ptr<Statement>> body) {
+ body_ = std::move(body);
+ }
+ /// @returns the case body
+ const std::vector<std::unique_ptr<Statement>>& body() const { return body_; }
+
+ /// @returns true if this is a case statement
+ bool IsCase() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ CaseStatement(const CaseStatement&) = delete;
+
+ std::unique_ptr<Literal> condition_;
+ std::vector<std::unique_ptr<Statement>> body_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_CASE_STATEMENT_H_
diff --git a/src/ast/cast_expression.cc b/src/ast/cast_expression.cc
new file mode 100644
index 0000000..1d52350
--- /dev/null
+++ b/src/ast/cast_expression.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 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/ast/cast_expression.h"
+
+namespace tint {
+namespace ast {
+
+CastExpression::CastExpression(type::Type* type,
+ std::unique_ptr<Expression> expr)
+ : Expression(), type_(type), expr_(std::move(expr)) {}
+
+CastExpression::CastExpression(const Source& source,
+ type::Type* type,
+ std::unique_ptr<Expression> expr)
+ : Expression(source), type_(type), expr_(std::move(expr)) {}
+
+CastExpression::~CastExpression() = default;
+
+bool CastExpression::IsValid() const {
+ return type_ != nullptr && expr_ != nullptr;
+}
+
+void CastExpression::to_str(std::ostream& out, size_t indent) const {
+ out << "cast<" << type_->type_name() << ">(";
+ expr_->to_str(out, indent);
+ out << ")";
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/cast_expression.h b/src/ast/cast_expression.h
new file mode 100644
index 0000000..dcb8a3f
--- /dev/null
+++ b/src/ast/cast_expression.h
@@ -0,0 +1,80 @@
+// Copyright 2020 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_AST_CAST_EXPRESSION_H_
+#define SRC_AST_CAST_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+
+/// A cast expression
+class CastExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param type the type
+ /// @param expr the expr
+ CastExpression(type::Type* type, std::unique_ptr<Expression> expr);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param type the type
+ /// @param expr the expr
+ CastExpression(const Source& source,
+ type::Type* type,
+ std::unique_ptr<Expression> expr);
+ /// Move constructor
+ CastExpression(CastExpression&&) = default;
+ ~CastExpression() override;
+
+ /// Sets the type
+ /// @param type the type
+ void set_type(type::Type* type) { type_ = std::move(type); }
+ /// @returns the left side expression
+ type::Type* type() const { return type_; }
+
+ /// Sets the expr
+ /// @param expr the expression
+ void set_expr(std::unique_ptr<Expression> expr) { expr_ = std::move(expr); }
+ /// @returns the expression
+ Expression* expr() const { return expr_.get(); }
+
+ /// @returns true if this is a cast expression
+ bool IsCast() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ CastExpression(const CastExpression&) = delete;
+
+ type::Type* type_;
+ std::unique_ptr<Expression> expr_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_CAST_EXPRESSION_H_
diff --git a/src/ast/const_initializer_expression.cc b/src/ast/const_initializer_expression.cc
new file mode 100644
index 0000000..11f1880
--- /dev/null
+++ b/src/ast/const_initializer_expression.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 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/ast/const_initializer_expression.h"
+
+namespace tint {
+namespace ast {
+
+ConstInitializerExpression::ConstInitializerExpression(
+ std::unique_ptr<Literal> literal)
+ : InitializerExpression(), literal_(std::move(literal)) {}
+
+ConstInitializerExpression::ConstInitializerExpression(
+ const Source& source,
+ std::unique_ptr<Literal> litearl)
+ : InitializerExpression(source), literal_(std::move(litearl)) {}
+
+ConstInitializerExpression::~ConstInitializerExpression() = default;
+
+bool ConstInitializerExpression::IsValid() const {
+ return literal_ != nullptr;
+}
+
+void ConstInitializerExpression::to_str(std::ostream& out,
+ size_t indent) const {
+ make_indent(out, indent);
+ out << "ConstInitializer{" << literal_->to_str() << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/const_initializer_expression.h b/src/ast/const_initializer_expression.h
new file mode 100644
index 0000000..31e2d44
--- /dev/null
+++ b/src/ast/const_initializer_expression.h
@@ -0,0 +1,70 @@
+// Copyright 2020 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_AST_CONST_INITIALIZER_EXPRESSION_H_
+#define SRC_AST_CONST_INITIALIZER_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/initializer_expression.h"
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// A constant initializer
+class ConstInitializerExpression : public InitializerExpression {
+ public:
+ /// Constructor
+ /// @param literal the const literal
+ explicit ConstInitializerExpression(std::unique_ptr<Literal> literal);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param literal the const literal
+ ConstInitializerExpression(const Source& source,
+ std::unique_ptr<Literal> literal);
+ /// Move constructor
+ ConstInitializerExpression(ConstInitializerExpression&&) = default;
+ ~ConstInitializerExpression() override;
+
+ /// @returns true if this is a constant initializer
+ bool IsConstInitializer() const override { return true; }
+
+ /// Set the literal value
+ /// @param literal the literal
+ void set_literal(std::unique_ptr<Literal> literal) {
+ literal_ = std::move(literal);
+ }
+ /// @returns the literal value
+ Literal* literal() const { return literal_.get(); }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ ConstInitializerExpression(const ConstInitializerExpression&) = delete;
+
+ std::unique_ptr<Literal> literal_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_CONST_INITIALIZER_EXPRESSION_H_
diff --git a/src/ast/continue_statement.cc b/src/ast/continue_statement.cc
new file mode 100644
index 0000000..e154455
--- /dev/null
+++ b/src/ast/continue_statement.cc
@@ -0,0 +1,57 @@
+// Copyright 2020 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/ast/continue_statement.h"
+
+namespace tint {
+namespace ast {
+
+ContinueStatement::ContinueStatement(StatementCondition condition,
+ std::unique_ptr<Expression> conditional)
+ : Statement(),
+ condition_(condition),
+ conditional_(std::move(conditional)) {}
+
+ContinueStatement::ContinueStatement(const Source& source,
+ StatementCondition condition,
+ std::unique_ptr<Expression> conditional)
+ : Statement(source),
+ condition_(condition),
+ conditional_(std::move(conditional)) {}
+
+ContinueStatement::~ContinueStatement() = default;
+
+bool ContinueStatement::IsValid() const {
+ return condition_ == StatementCondition::kNone || conditional_ != nullptr;
+}
+
+void ContinueStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Continue";
+
+ if (condition_ != StatementCondition::kNone) {
+ out << "{" << std::endl;
+
+ make_indent(out, indent + 2);
+ out << condition_ << std::endl;
+ conditional_->to_str(out, indent + 2);
+
+ make_indent(out, indent);
+ out << "}";
+ }
+ out << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/continue_statement.h b/src/ast/continue_statement.h
new file mode 100644
index 0000000..8cadbb7
--- /dev/null
+++ b/src/ast/continue_statement.h
@@ -0,0 +1,84 @@
+// Copyright 2020 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_AST_CONTINUE_STATEMENT_H_
+#define SRC_AST_CONTINUE_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+#include "src/ast/statement_condition.h"
+
+namespace tint {
+namespace ast {
+
+/// An continue statement
+class ContinueStatement : public Statement {
+ public:
+ /// Constructor
+ ContinueStatement();
+ /// Constructor
+ /// @param condition the condition type
+ /// @param conditional the condition expression
+ ContinueStatement(StatementCondition condition,
+ std::unique_ptr<Expression> conditional);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param condition the condition type
+ /// @param conditional the condition expression
+ ContinueStatement(const Source& source,
+ StatementCondition condition,
+ std::unique_ptr<Expression> conditional);
+ /// Move constructor
+ ContinueStatement(ContinueStatement&&) = default;
+ ~ContinueStatement() override;
+
+ /// Sets the condition type
+ /// @param condition the condition type
+ void set_condition(StatementCondition condition) { condition_ = condition; }
+ /// @returns the condition type
+ StatementCondition condition() const { return condition_; }
+
+ /// Sets the conditional expression
+ /// @param conditional the conditional expression
+ void set_conditional(std::unique_ptr<Expression> conditional) {
+ conditional_ = std::move(conditional);
+ }
+ /// @returns the conditional expression
+ Expression* conditional() const { return conditional_.get(); }
+
+ /// @returns true if this is an continue statement
+ bool IsContinue() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ ContinueStatement(const ContinueStatement&) = delete;
+
+ StatementCondition condition_ = StatementCondition::kNone;
+ std::unique_ptr<Expression> conditional_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_CONTINUE_STATEMENT_H_
diff --git a/src/ast/decorated_variable.cc b/src/ast/decorated_variable.cc
new file mode 100644
index 0000000..2a6b42e
--- /dev/null
+++ b/src/ast/decorated_variable.cc
@@ -0,0 +1,51 @@
+// Copyright 2020 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/ast/decorated_variable.h"
+
+namespace tint {
+namespace ast {
+
+DecoratedVariable::DecoratedVariable() = default;
+
+DecoratedVariable::DecoratedVariable(DecoratedVariable&&) = default;
+
+DecoratedVariable::~DecoratedVariable() = default;
+
+bool DecoratedVariable::IsValid() const {
+ return Variable::IsValid();
+}
+
+void DecoratedVariable::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "DecoratedVariable{" << std::endl;
+
+ make_indent(out, indent + 2);
+ out << "decorations{" << std::endl;
+ for (const auto& deco : decorations_) {
+ make_indent(out, indent + 4);
+ deco->to_str(out);
+ out << std::endl;
+ }
+
+ make_indent(out, indent + 2);
+ out << "}" << std::endl;
+
+ info_to_str(out, indent + 2);
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/decorated_variable.h b/src/ast/decorated_variable.h
new file mode 100644
index 0000000..d81de81
--- /dev/null
+++ b/src/ast/decorated_variable.h
@@ -0,0 +1,67 @@
+// Copyright 2020 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_AST_DECORATED_VARIABLE_H_
+#define SRC_AST_DECORATED_VARIABLE_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/variable.h"
+#include "src/ast/variable_decoration.h"
+
+namespace tint {
+namespace ast {
+
+/// A Decorated Variable statement.
+class DecoratedVariable : public Variable {
+ public:
+ /// Create a new empty decorated variable statement
+ DecoratedVariable();
+ /// Move constructor
+ DecoratedVariable(DecoratedVariable&&);
+
+ ~DecoratedVariable() override;
+
+ /// Sets a decoration to the variable
+ /// @param decos the decorations to set
+ void set_decorations(std::vector<std::unique_ptr<VariableDecoration>> decos) {
+ decorations_ = std::move(decos);
+ }
+ /// @returns the decorations attached to this variable
+ const std::vector<std::unique_ptr<VariableDecoration>>& decorations() const {
+ return decorations_;
+ }
+
+ /// @returns true if this is a decorated variable
+ bool IsDecorated() const override { return true; }
+
+ /// @returns true if the name and path are both present
+ bool IsValid() const override;
+
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ DecoratedVariable(const DecoratedVariable&) = delete;
+
+ std::vector<std::unique_ptr<VariableDecoration>> decorations_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_DECORATED_VARIABLE_H_
diff --git a/src/ast/derivative_modifier.cc b/src/ast/derivative_modifier.cc
new file mode 100644
index 0000000..66a7f35
--- /dev/null
+++ b/src/ast/derivative_modifier.cc
@@ -0,0 +1,39 @@
+// Copyright 2020 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/ast/derivative_modifier.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, DerivativeModifier mod) {
+ switch (mod) {
+ case DerivativeModifier::kNone: {
+ out << "none";
+ break;
+ }
+ case DerivativeModifier::kFine: {
+ out << "fine";
+ break;
+ }
+ case DerivativeModifier::kCoarse: {
+ out << "coarse";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/derivative_modifier.h b/src/ast/derivative_modifier.h
new file mode 100644
index 0000000..83789e4
--- /dev/null
+++ b/src/ast/derivative_modifier.h
@@ -0,0 +1,31 @@
+// Copyright 2020 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_AST_DERIVATIVE_MODIFIER_H_
+#define SRC_AST_DERIVATIVE_MODIFIER_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// The derivative modifier
+enum class DerivativeModifier { kNone = -1, kFine, kCoarse };
+
+std::ostream& operator<<(std::ostream& out, DerivativeModifier mod);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_DERIVATIVE_MODIFIER_H_
diff --git a/src/ast/else_statement.cc b/src/ast/else_statement.cc
new file mode 100644
index 0000000..e41f61b
--- /dev/null
+++ b/src/ast/else_statement.cc
@@ -0,0 +1,63 @@
+// Copyright 2020 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/ast/else_statement.h"
+
+namespace tint {
+namespace ast {
+
+ElseStatement::ElseStatement() : Statement() {}
+
+ElseStatement::ElseStatement(std::vector<std::unique_ptr<Statement>> body)
+ : Statement(), body_(std::move(body)) {}
+
+ElseStatement::ElseStatement(std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(), condition_(std::move(condition)), body_(std::move(body)) {}
+
+ElseStatement::ElseStatement(const Source& source,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(source), body_(std::move(body)) {}
+
+ElseStatement::ElseStatement(const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(source),
+ condition_(std::move(condition)),
+ body_(std::move(body)) {}
+
+ElseStatement::~ElseStatement() = default;
+
+bool ElseStatement::IsValid() const {
+ return true;
+}
+
+void ElseStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Else{" << std::endl;
+ if (condition_ != nullptr)
+ condition_->to_str(out, indent + 2);
+
+ make_indent(out, indent + 2);
+ out << "{" << std::endl;
+
+ for (const auto& stmt : body_)
+ stmt->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/else_statement.h b/src/ast/else_statement.h
new file mode 100644
index 0000000..66511f3
--- /dev/null
+++ b/src/ast/else_statement.h
@@ -0,0 +1,96 @@
+// Copyright 2020 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_AST_ELSE_STATEMENT_H_
+#define SRC_AST_ELSE_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// An else statement
+class ElseStatement : public Statement {
+ public:
+ /// Constructor
+ ElseStatement();
+ /// Constructor
+ /// @param body the else body
+ explicit ElseStatement(std::vector<std::unique_ptr<Statement>> body);
+ /// Constructor
+ /// @param condition the else condition
+ /// @param body the else body
+ ElseStatement(std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Constructor
+ /// @param source the source information
+ /// @param body the else body
+ ElseStatement(const Source& source,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Constructor
+ /// @param source the source information
+ /// @param condition the else condition
+ /// @param body the else body
+ ElseStatement(const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Move constructor
+ ElseStatement(ElseStatement&&) = default;
+ ~ElseStatement() override;
+
+ /// Sets the condition for the else statement
+ /// @param condition the condition to set
+ void set_condition(std::unique_ptr<Expression> condition) {
+ condition_ = std::move(condition);
+ }
+ /// @returns the else condition or nullptr if none set
+ Expression* condition() const { return condition_.get(); }
+ /// @returns true if the else has a condition
+ bool HasCondition() const { return condition_ != nullptr; }
+
+ /// Sets the else body
+ /// @param body the else body
+ void set_body(std::vector<std::unique_ptr<Statement>> body) {
+ body_ = std::move(body);
+ }
+ /// @returns the else body
+ const std::vector<std::unique_ptr<Statement>>& body() const { return body_; }
+
+ /// @returns true if this is a else statement
+ bool IsElse() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ ElseStatement(const ElseStatement&) = delete;
+
+ std::unique_ptr<Expression> condition_;
+ std::vector<std::unique_ptr<Statement>> body_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_ELSE_STATEMENT_H_
diff --git a/src/ast/entry_point.cc b/src/ast/entry_point.cc
new file mode 100644
index 0000000..a433be1
--- /dev/null
+++ b/src/ast/entry_point.cc
@@ -0,0 +1,50 @@
+// Copyright 2020 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/ast/entry_point.h"
+
+namespace tint {
+namespace ast {
+
+EntryPoint::EntryPoint(PipelineStage stage,
+ const std::string& name,
+ const std::string& fn_name)
+ : Node(), stage_(stage), name_(name), fn_name_(fn_name) {}
+
+EntryPoint::EntryPoint(const Source& source,
+ PipelineStage stage,
+ const std::string& name,
+ const std::string& fn_name)
+ : Node(source), stage_(stage), name_(name), fn_name_(fn_name) {}
+
+EntryPoint::~EntryPoint() = default;
+
+bool EntryPoint::IsValid() const {
+ if (stage_ == PipelineStage::kNone) {
+ return false;
+ }
+ if (fn_name_.length() == 0) {
+ return false;
+ }
+ return true;
+}
+
+void EntryPoint::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << R"(EntryPoint{")" << stage_ << R"(" as ")" << name_ << R"(" = )"
+ << fn_name_ << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/entry_point.h b/src/ast/entry_point.h
new file mode 100644
index 0000000..40ca20f
--- /dev/null
+++ b/src/ast/entry_point.h
@@ -0,0 +1,89 @@
+// Copyright 2020 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_AST_ENTRY_POINT_H_
+#define SRC_AST_ENTRY_POINT_H_
+
+#include <ostream>
+#include <string>
+
+#include "src/ast/node.h"
+#include "src/ast/pipeline_stage.h"
+
+namespace tint {
+namespace ast {
+
+/// An entry point statement.
+class EntryPoint : public Node {
+ public:
+ /// Constructor
+ EntryPoint() = default;
+ /// Constructor
+ /// @param stage the entry point stage
+ /// @param name the entry point name
+ /// @param fn_name the function name
+ EntryPoint(PipelineStage stage,
+ const std::string& name,
+ const std::string& fn_name);
+ /// Constructor
+ /// @param source the source of the entry point
+ /// @param stage the entry point stage
+ /// @param name the entry point name
+ /// @param fn_name the function name
+ EntryPoint(const Source& source,
+ PipelineStage stage,
+ const std::string& name,
+ const std::string& fn_name);
+ /// Move constructor
+ EntryPoint(EntryPoint&&) = default;
+
+ ~EntryPoint() override;
+
+ /// Sets the entry point name
+ /// @param name the name to set
+ void set_name(const std::string& name) { name_ = name; }
+ /// @returns the entry points name
+ const std::string& name() const { return name_; }
+ /// Sets the entry point function name
+ /// @param name the function name
+ void set_function_name(const std::string& name) { fn_name_ = name; }
+ /// @returns the function name for the entry point
+ const std::string& function_name() const { return fn_name_; }
+ /// Sets the piepline stage
+ /// @param stage the stage to set
+ void set_pipeline_stage(PipelineStage stage) { stage_ = stage; }
+ /// @returns the pipeline stage for the entry point
+ PipelineStage stage() const { return stage_; }
+
+ /// @returns true if the entry point is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the entry point to the output stream
+ /// @param out the stream to write too
+ /// @param indent number of spaces to ident the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ EntryPoint(const EntryPoint&) = delete;
+
+ Source source_;
+ PipelineStage stage_;
+ std::string name_;
+ std::string fn_name_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_ENTRY_POINT_H_
diff --git a/src/ast/entry_point_test.cc b/src/ast/entry_point_test.cc
new file mode 100644
index 0000000..5765924
--- /dev/null
+++ b/src/ast/entry_point_test.cc
@@ -0,0 +1,91 @@
+// Copyright 2020 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/ast/entry_point.h"
+
+#include <sstream>
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using EntryPointTest = testing::Test;
+
+TEST_F(EntryPointTest, Creation) {
+ EntryPoint e(PipelineStage::kVertex, "main", "vtx_main");
+
+ EXPECT_EQ(e.name(), "main");
+ EXPECT_EQ(e.function_name(), "vtx_main");
+ EXPECT_EQ(e.stage(), PipelineStage::kVertex);
+ EXPECT_EQ(e.line(), 0);
+ EXPECT_EQ(e.column(), 0);
+}
+
+TEST_F(EntryPointTest, CreationWithSource) {
+ Source s{27, 4};
+ EntryPoint e(s, PipelineStage::kVertex, "main", "vtx_main");
+
+ EXPECT_EQ(e.name(), "main");
+ EXPECT_EQ(e.function_name(), "vtx_main");
+ EXPECT_EQ(e.stage(), PipelineStage::kVertex);
+ EXPECT_EQ(e.line(), 27);
+ EXPECT_EQ(e.column(), 4);
+}
+
+TEST_F(EntryPointTest, CreationEmpty) {
+ Source s{27, 4};
+ EntryPoint e;
+ e.set_source(s);
+ e.set_pipeline_stage(PipelineStage::kFragment);
+ e.set_function_name("my_func");
+ e.set_name("a_name");
+
+ EXPECT_EQ(e.function_name(), "my_func");
+ EXPECT_EQ(e.name(), "a_name");
+ EXPECT_EQ(e.stage(), PipelineStage::kFragment);
+ EXPECT_EQ(e.line(), 27);
+ EXPECT_EQ(e.column(), 4);
+}
+
+TEST_F(EntryPointTest, to_str) {
+ EntryPoint e(PipelineStage::kVertex, "text", "vtx_main");
+ std::ostringstream out;
+ e.to_str(out, 0);
+ EXPECT_EQ(out.str(), R"(EntryPoint{"vertex" as "text" = vtx_main}
+)");
+}
+
+TEST_F(EntryPointTest, IsValid) {
+ EntryPoint e(PipelineStage::kVertex, "main", "vtx_main");
+ EXPECT_TRUE(e.IsValid());
+}
+
+TEST_F(EntryPointTest, IsValid_MissingFunctionName) {
+ EntryPoint e(PipelineStage::kVertex, "main", "");
+ EXPECT_FALSE(e.IsValid());
+}
+
+TEST_F(EntryPointTest, IsValid_MissingStage) {
+ EntryPoint e(PipelineStage::kNone, "main", "fn");
+ EXPECT_FALSE(e.IsValid());
+}
+
+TEST_F(EntryPointTest, IsValid_MissingBoth) {
+ EntryPoint e;
+ EXPECT_FALSE(e.IsValid());
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/expression.cc b/src/ast/expression.cc
new file mode 100644
index 0000000..78041d6
--- /dev/null
+++ b/src/ast/expression.cc
@@ -0,0 +1,96 @@
+// Copyright 2020 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/ast/expression.h"
+
+#include <assert.h>
+
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/as_expression.h"
+#include "src/ast/call_expression.h"
+#include "src/ast/cast_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/initializer_expression.h"
+#include "src/ast/member_accessor_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/ast/unary_derivative_expression.h"
+#include "src/ast/unary_method_expression.h"
+#include "src/ast/unary_op_expression.h"
+
+namespace tint {
+namespace ast {
+
+Expression::Expression() = default;
+
+Expression::Expression(const Source& source) : Node(source) {}
+
+Expression::~Expression() = default;
+
+ArrayAccessorExpression* Expression::AsArrayAccessor() {
+ assert(IsArrayAccessor());
+ return static_cast<ArrayAccessorExpression*>(this);
+}
+
+AsExpression* Expression::AsAs() {
+ assert(IsAs());
+ return static_cast<AsExpression*>(this);
+}
+
+CallExpression* Expression::AsCall() {
+ assert(IsCall());
+ return static_cast<CallExpression*>(this);
+}
+
+CastExpression* Expression::AsCast() {
+ assert(IsCast());
+ return static_cast<CastExpression*>(this);
+}
+
+IdentifierExpression* Expression::AsIdentifier() {
+ assert(IsIdentifier());
+ return static_cast<IdentifierExpression*>(this);
+}
+
+InitializerExpression* Expression::AsInitializer() {
+ assert(IsInitializer());
+ return static_cast<InitializerExpression*>(this);
+}
+
+MemberAccessorExpression* Expression::AsMemberAccessor() {
+ assert(IsMemberAccessor());
+ return static_cast<MemberAccessorExpression*>(this);
+}
+
+RelationalExpression* Expression::AsRelational() {
+ assert(IsRelational());
+ return static_cast<RelationalExpression*>(this);
+}
+
+UnaryDerivativeExpression* Expression::AsUnaryDerivative() {
+ assert(IsUnaryDerivative());
+ return static_cast<UnaryDerivativeExpression*>(this);
+}
+
+UnaryMethodExpression* Expression::AsUnaryMethod() {
+ assert(IsUnaryMethod());
+ return static_cast<UnaryMethodExpression*>(this);
+}
+
+UnaryOpExpression* Expression::AsUnaryOp() {
+ assert(IsUnaryOp());
+ return static_cast<UnaryOpExpression*>(this);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/expression.h b/src/ast/expression.h
new file mode 100644
index 0000000..ebc694b
--- /dev/null
+++ b/src/ast/expression.h
@@ -0,0 +1,102 @@
+// Copyright 2020 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_AST_EXPRESSION_H_
+#define SRC_AST_EXPRESSION_H_
+
+#include "src/ast/node.h"
+
+namespace tint {
+namespace ast {
+
+class ArrayAccessorExpression;
+class AsExpression;
+class CallExpression;
+class CastExpression;
+class IdentifierExpression;
+class InitializerExpression;
+class MemberAccessorExpression;
+class RelationalExpression;
+class UnaryDerivativeExpression;
+class UnaryMethodExpression;
+class UnaryOpExpression;
+
+/// Base expression class
+class Expression : public Node {
+ public:
+ ~Expression() override;
+
+ /// @returns true if this is an array accessor expression
+ virtual bool IsArrayAccessor() const { return false; }
+ /// @returns true if this is an as expression
+ virtual bool IsAs() const { return false; }
+ /// @returns true if this is a call expression
+ virtual bool IsCall() const { return false; }
+ /// @returns true if this is a cast expression
+ virtual bool IsCast() const { return false; }
+ /// @returns true if this is an identifier expression
+ virtual bool IsIdentifier() const { return false; }
+ /// @returns true if this is an initializer expression
+ virtual bool IsInitializer() const { return false; }
+ /// @returns true if this is a member accessor expression
+ virtual bool IsMemberAccessor() const { return false; }
+ /// @returns true if this is a relational expression
+ virtual bool IsRelational() const { return false; }
+ /// @returns true if this is a unary derivative expression
+ virtual bool IsUnaryDerivative() const { return false; }
+ /// @returns true if this is a unary method expression
+ virtual bool IsUnaryMethod() const { return false; }
+ /// @returns true if this is a unary op expression
+ virtual bool IsUnaryOp() const { return false; }
+
+ /// @returns the expression as an array accessor
+ ArrayAccessorExpression* AsArrayAccessor();
+ /// @returns the expression as an as
+ AsExpression* AsAs();
+ /// @returns the expression as a call
+ CallExpression* AsCall();
+ /// @returns the expression as a cast
+ CastExpression* AsCast();
+ /// @returns the expression as an identifier
+ IdentifierExpression* AsIdentifier();
+ /// @returns the expression as an initializer
+ InitializerExpression* AsInitializer();
+ /// @returns the expression as a member accessor
+ MemberAccessorExpression* AsMemberAccessor();
+ /// @returns the expression as a relational expression
+ RelationalExpression* AsRelational();
+ /// @returns the expression as a unary derivative expression
+ UnaryDerivativeExpression* AsUnaryDerivative();
+ /// @returns the expression as a unary method expression
+ UnaryMethodExpression* AsUnaryMethod();
+ /// @returns the expression as a unary op expression
+ UnaryOpExpression* AsUnaryOp();
+
+ protected:
+ /// Constructor
+ Expression();
+ /// Constructor
+ /// @param source the source of the expression
+ explicit Expression(const Source& source);
+ /// Move constructor
+ Expression(Expression&&) = default;
+
+ private:
+ Expression(const Expression&) = delete;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_EXPRESSION_H_
diff --git a/src/ast/fallthrough_statement.cc b/src/ast/fallthrough_statement.cc
new file mode 100644
index 0000000..f5ddcea
--- /dev/null
+++ b/src/ast/fallthrough_statement.cc
@@ -0,0 +1,37 @@
+// Copyright 2020 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/ast/fallthrough_statement.h"
+
+namespace tint {
+namespace ast {
+
+FallthroughStatement::FallthroughStatement() : Statement() {}
+
+FallthroughStatement::FallthroughStatement(const Source& source)
+ : Statement(source) {}
+
+FallthroughStatement::~FallthroughStatement() = default;
+
+bool FallthroughStatement::IsValid() const {
+ return true;
+}
+
+void FallthroughStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Fallthrough" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/fallthrough_statement.h b/src/ast/fallthrough_statement.h
new file mode 100644
index 0000000..34cd092
--- /dev/null
+++ b/src/ast/fallthrough_statement.h
@@ -0,0 +1,58 @@
+// Copyright 2020 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_AST_FALLTHROUGH_STATEMENT_H_
+#define SRC_AST_FALLTHROUGH_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+#include "src/ast/statement_condition.h"
+
+namespace tint {
+namespace ast {
+
+/// An fallthrough statement
+class FallthroughStatement : public Statement {
+ public:
+ /// Constructor
+ FallthroughStatement();
+ /// Constructor
+ /// @param source the source information
+ explicit FallthroughStatement(const Source& source);
+ /// Move constructor
+ FallthroughStatement(FallthroughStatement&&) = default;
+ ~FallthroughStatement() override;
+
+ /// @returns true if this is an fallthrough statement
+ bool IsFallthrough() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ FallthroughStatement(const FallthroughStatement&) = delete;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_FALLTHROUGH_STATEMENT_H_
diff --git a/src/ast/float_literal.cc b/src/ast/float_literal.cc
new file mode 100644
index 0000000..1d8b40f
--- /dev/null
+++ b/src/ast/float_literal.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 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/ast/float_literal.h"
+
+namespace tint {
+namespace ast {
+
+FloatLiteral::FloatLiteral(float value) : value_(value) {}
+
+FloatLiteral::~FloatLiteral() = default;
+
+std::string FloatLiteral::to_str() const {
+ return std::to_string(value_);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/float_literal.h b/src/ast/float_literal.h
new file mode 100644
index 0000000..baf088a
--- /dev/null
+++ b/src/ast/float_literal.h
@@ -0,0 +1,49 @@
+// Copyright 2020 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_AST_FLOAT_LITERAL_H_
+#define SRC_AST_FLOAT_LITERAL_H_
+
+#include <string>
+
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// A float literal
+class FloatLiteral : public Literal {
+ public:
+ /// Constructor
+ /// @param value the float literals value
+ explicit FloatLiteral(float value);
+ ~FloatLiteral() override;
+
+ /// @returns true if this is a float literal
+ bool IsFloat() const override { return true; }
+
+ /// @returns the float literal value
+ float value() const { return value_; }
+
+ /// @returns the literal as a string
+ std::string to_str() const override;
+
+ private:
+ float value_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_FLOAT_LITERAL_H_
diff --git a/src/ast/float_literal_test.cc b/src/ast/float_literal_test.cc
new file mode 100644
index 0000000..8274761
--- /dev/null
+++ b/src/ast/float_literal_test.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 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/ast/float_literal.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using FloatLiteralTest = testing::Test;
+
+TEST_F(FloatLiteralTest, Value) {
+ FloatLiteral f{47.2};
+ ASSERT_TRUE(f.IsFloat());
+ EXPECT_EQ(f.value(), 47.2);
+}
+
+TEST_F(FloatLiteralTest, Is) {
+ FloatLiteral f{42};
+ EXPECT_FALSE(f.IsBool());
+ EXPECT_FALSE(f.IsInt());
+ EXPECT_TRUE(f.IsFloat());
+ EXPECT_FALSE(f.IsUint());
+}
+
+TEST_F(FloatLiteralTest, ToStr) {
+ FloatLiteral f{42.1};
+
+ EXPECT_EQ(f.to_str(), "42.1");
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/function.cc b/src/ast/function.cc
new file mode 100644
index 0000000..f53ba0b
--- /dev/null
+++ b/src/ast/function.cc
@@ -0,0 +1,71 @@
+// Copyright 2020 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/ast/function.h"
+
+namespace tint {
+namespace ast {
+
+Function::Function(const std::string& name,
+ std::vector<std::unique_ptr<Variable>> params,
+ type::Type* return_type)
+ : Node(),
+ name_(name),
+ params_(std::move(params)),
+ return_type_(return_type) {}
+
+Function::Function(const Source& source,
+ const std::string& name,
+ std::vector<std::unique_ptr<Variable>> params,
+ type::Type* return_type)
+ : Node(source),
+ name_(name),
+ params_(std::move(params)),
+ return_type_(return_type) {}
+
+Function::~Function() = default;
+
+bool Function::IsValid() const {
+ if (name_.length() == 0) {
+ return false;
+ }
+ if (return_type_ == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+void Function::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "function -> " << return_type_->type_name() << "{" << std::endl;
+ make_indent(out, indent + 2);
+ out << name_ << std::endl;
+
+ for (const auto& param : params_)
+ param->to_str(out, indent + 2);
+
+ make_indent(out, indent + 2);
+ out << "{" << std::endl;
+
+ for (const auto& stmt : body_)
+ stmt->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << "}" << std::endl;
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/function.h b/src/ast/function.h
new file mode 100644
index 0000000..6a1158b
--- /dev/null
+++ b/src/ast/function.h
@@ -0,0 +1,109 @@
+// Copyright 2020 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_AST_FUNCTION_H_
+#define SRC_AST_FUNCTION_H_
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/node.h"
+#include "src/ast/statement.h"
+#include "src/ast/type/type.h"
+#include "src/ast/variable.h"
+
+namespace tint {
+namespace ast {
+
+/// A Function statement.
+class Function : public Node {
+ public:
+ /// Create a new empty function statement
+ Function() = default;
+ /// Create a function
+ /// @param name the function name
+ /// @param params the function parameters
+ /// @param return_type the return type
+ Function(const std::string& name,
+ std::vector<std::unique_ptr<Variable>> params,
+ type::Type* return_type);
+ /// Create a function
+ /// @param source the variable source
+ /// @param name the function name
+ /// @param params the function parameters
+ /// @param return_type the return type
+ Function(const Source& source,
+ const std::string& name,
+ std::vector<std::unique_ptr<Variable>> params,
+ type::Type* return_type);
+ /// Move constructor
+ Function(Function&&) = default;
+
+ ~Function() override;
+
+ /// Sets the function name
+ /// @param name the name to set
+ void set_name(const std::string& name) { name_ = name; }
+ /// @returns the function name
+ const std::string& name() { return name_; }
+
+ /// Sets the function parameters
+ /// @param params the function parameters
+ void set_params(std::vector<std::unique_ptr<Variable>> params) {
+ params_ = std::move(params);
+ }
+ /// @returns the function params
+ const std::vector<std::unique_ptr<Variable>>& params() const {
+ return params_;
+ }
+
+ /// Sets the return type of the function
+ /// @param type the return type
+ void set_return_type(type::Type* type) { return_type_ = type; }
+ /// @returns the function return type.
+ type::Type* return_type() const { return return_type_; }
+
+ /// Sets the body of the function
+ /// @param body the function body
+ void set_body(std::vector<std::unique_ptr<Statement>> body) {
+ body_ = std::move(body);
+ }
+ /// @returns the function body
+ const std::vector<std::unique_ptr<Statement>>& body() const { return body_; }
+
+ /// @returns true if the name and path are both present
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ Function(const Function&) = delete;
+
+ std::string name_;
+ std::vector<std::unique_ptr<Variable>> params_;
+ type::Type* return_type_ = nullptr;
+ std::vector<std::unique_ptr<Statement>> body_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_FUNCTION_H_
diff --git a/src/ast/identifier_expression.cc b/src/ast/identifier_expression.cc
new file mode 100644
index 0000000..9fcf4c5
--- /dev/null
+++ b/src/ast/identifier_expression.cc
@@ -0,0 +1,55 @@
+// Copyright 2020 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/ast/identifier_expression.h"
+
+namespace tint {
+namespace ast {
+
+IdentifierExpression::IdentifierExpression(const std::string& name)
+ : Expression(), name_({name}) {}
+
+IdentifierExpression::IdentifierExpression(const Source& source,
+ const std::string& name)
+ : Expression(source), name_({name}) {}
+
+IdentifierExpression::IdentifierExpression(std::vector<std::string> name)
+ : Expression(), name_(std::move(name)) {}
+
+IdentifierExpression::IdentifierExpression(const Source& source,
+ std::vector<std::string> name)
+ : Expression(source), name_(std::move(name)) {}
+
+IdentifierExpression::~IdentifierExpression() = default;
+
+bool IdentifierExpression::IsValid() const {
+ return name_.size() > 0 && name_[1].size() > 0;
+}
+
+void IdentifierExpression::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Identifier{";
+ bool first = true;
+ for (const auto& name : name_) {
+ if (!first)
+ out << "::";
+
+ first = false;
+ out << name;
+ }
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/identifier_expression.h b/src/ast/identifier_expression.h
new file mode 100644
index 0000000..8efe83c
--- /dev/null
+++ b/src/ast/identifier_expression.h
@@ -0,0 +1,75 @@
+// Copyright 2020 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_AST_IDENTIFIER_EXPRESSION_H_
+#define SRC_AST_IDENTIFIER_EXPRESSION_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+
+namespace tint {
+namespace ast {
+
+/// An identifier expression
+class IdentifierExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param name the name
+ explicit IdentifierExpression(const std::string& name);
+ /// Constructor
+ /// @param source the source
+ /// @param name the name
+ IdentifierExpression(const Source& source, const std::string& name);
+ /// Constructor
+ /// @param name the name
+ explicit IdentifierExpression(std::vector<std::string> name);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param name the name
+ IdentifierExpression(const Source& source, std::vector<std::string> name);
+ /// Move constructor
+ IdentifierExpression(IdentifierExpression&&) = default;
+ ~IdentifierExpression() override;
+
+ /// Sets the name
+ /// @param name the name
+ void set_name(std::vector<std::string> name) { name_ = std::move(name); }
+ /// @returns the name
+ std::vector<std::string> name() const { return name_; }
+
+ /// @returns true if this is an identifier expression
+ bool IsIdentifier() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ IdentifierExpression(const IdentifierExpression&) = delete;
+
+ std::vector<std::string> name_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_IDENTIFIER_EXPRESSION_H_
diff --git a/src/ast/if_statement.cc b/src/ast/if_statement.cc
new file mode 100644
index 0000000..364b828
--- /dev/null
+++ b/src/ast/if_statement.cc
@@ -0,0 +1,77 @@
+// Copyright 2020 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/ast/if_statement.h"
+
+#include "src/ast/else_statement.h"
+
+namespace tint {
+namespace ast {
+
+IfStatement::IfStatement() : Statement() {}
+
+IfStatement::IfStatement(std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(), condition_(std::move(condition)), body_(std::move(body)) {}
+
+IfStatement::IfStatement(const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(source),
+ condition_(std::move(condition)),
+ body_(std::move(body)) {}
+
+IfStatement::~IfStatement() = default;
+
+bool IfStatement::IsValid() const {
+ if (condition_ == nullptr)
+ return false;
+
+ if (premerge_.size() > 0 && else_statements_.size() > 1)
+ return false;
+
+ return true;
+}
+
+void IfStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "If{" << std::endl;
+ condition_->to_str(out, indent + 2);
+ out << std::endl;
+ make_indent(out, indent + 2);
+ out << "{" << std::endl;
+
+ for (const auto& stmt : body_)
+ stmt->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << "}" << std::endl;
+
+ for (const auto& e : else_statements_)
+ e->to_str(out, indent + 2);
+
+ if (premerge_.size() > 0) {
+ make_indent(out, indent + 2);
+ out << "premerge{" << std::endl;
+
+ for (const auto& stmt : premerge_)
+ stmt->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << "}" << std::endl;
+ }
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/if_statement.h b/src/ast/if_statement.h
new file mode 100644
index 0000000..f775f6e
--- /dev/null
+++ b/src/ast/if_statement.h
@@ -0,0 +1,109 @@
+// Copyright 2020 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_AST_IF_STATEMENT_H_
+#define SRC_AST_IF_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// An if statement
+class IfStatement : public Statement {
+ public:
+ /// Constructor
+ IfStatement();
+ /// Constructor
+ /// @param condition the if condition
+ /// @param body the if body
+ IfStatement(std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Constructor
+ /// @param source the source information
+ /// @param condition the if condition
+ /// @param body the if body
+ IfStatement(const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Move constructor
+ IfStatement(IfStatement&&) = default;
+ ~IfStatement() override;
+
+ /// Sets the condition for the if statement
+ /// @param condition the condition to set
+ void set_condition(std::unique_ptr<Expression> condition) {
+ condition_ = std::move(condition);
+ }
+ /// @returns the if condition or nullptr if none set
+ Expression* condition() const { return condition_.get(); }
+
+ /// Sets the if body
+ /// @param body the if body
+ void set_body(std::vector<std::unique_ptr<Statement>> body) {
+ body_ = std::move(body);
+ }
+ /// @returns the if body
+ const std::vector<std::unique_ptr<Statement>>& body() const { return body_; }
+
+ /// Sets the else statements
+ /// @param else_statements the else statements to set
+ void set_else_statements(
+ std::vector<std::unique_ptr<ElseStatement>> else_statements) {
+ else_statements_ = std::move(else_statements);
+ }
+ /// @returns the else statements
+ const std::vector<std::unique_ptr<ElseStatement>>& else_statements() const {
+ return else_statements_;
+ }
+
+ /// Sets the premerge statements
+ /// @param premerge the premerge statements
+ void set_premerge(std::vector<std::unique_ptr<Statement>> premerge) {
+ premerge_ = std::move(premerge);
+ }
+ /// @returns the premerge statements
+ const std::vector<std::unique_ptr<Statement>>& premerge() const {
+ return premerge_;
+ }
+
+ /// @returns true if this is a if statement
+ bool IsIf() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ IfStatement(const IfStatement&) = delete;
+
+ std::unique_ptr<Expression> condition_;
+ std::vector<std::unique_ptr<Statement>> body_;
+ std::vector<std::unique_ptr<ElseStatement>> else_statements_;
+ std::vector<std::unique_ptr<Statement>> premerge_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_IF_STATEMENT_H_
diff --git a/src/ast/import.cc b/src/ast/import.cc
new file mode 100644
index 0000000..aa7aff8
--- /dev/null
+++ b/src/ast/import.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 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/ast/import.h"
+
+namespace tint {
+namespace ast {
+
+Import::Import(const std::string& path, const std::string& name)
+ : Node(), path_(path), name_(name) {}
+
+Import::Import(const Source& source,
+ const std::string& path,
+ const std::string& name)
+ : Node(source), path_(path), name_(name) {}
+
+Import::~Import() = default;
+
+bool Import::IsValid() const {
+ if (path_.length() == 0) {
+ return false;
+ }
+
+ auto len = name_.length();
+ if (len == 0) {
+ return false;
+ }
+
+ // Verify the import name ends in a character, number or _
+ if (len > 2 && !std::isalnum(name_[len - 1]) && name_[len] != '_') {
+ return false;
+ }
+
+ return true;
+}
+
+void Import::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << R"(Import{")" + path_ + R"(" as )" + name_ + "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/import.h b/src/ast/import.h
new file mode 100644
index 0000000..cb67c25
--- /dev/null
+++ b/src/ast/import.h
@@ -0,0 +1,76 @@
+// Copyright 2020 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_AST_IMPORT_H_
+#define SRC_AST_IMPORT_H_
+
+#include <ostream>
+#include <string>
+
+#include "src/ast/node.h"
+
+namespace tint {
+namespace ast {
+
+/// An import statement.
+class Import : public Node {
+ public:
+ /// Create a new empty import statement
+ Import() = default;
+ /// Create a new import statement
+ /// @param path The import path e.g. GLSL.std.430
+ /// @param name The import reference name e.g. std::
+ Import(const std::string& path, const std::string& name);
+ /// Create a new import statement
+ /// @param source The input source for the import statement
+ /// @param path The import path e.g. GLSL.std.430
+ /// @param name The import reference name e.g. std::
+ Import(const Source& source,
+ const std::string& path,
+ const std::string& name);
+ /// Move constructor
+ Import(Import&&) = default;
+
+ ~Import() override;
+
+ /// Sets the import path
+ /// @param path the path to set
+ void set_path(const std::string& path) { path_ = path; }
+ /// @returns the import path
+ const std::string& path() const { return path_; }
+ /// Sets the import name
+ /// @param name the name to set
+ void set_name(const std::string& name) { name_ = name; }
+ /// @returns the import name
+ const std::string& name() const { return name_; }
+
+ /// @returns true if the name and path are both present
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ Import(const Import&) = delete;
+
+ std::string path_;
+ std::string name_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_IMPORT_H_
diff --git a/src/ast/import_test.cc b/src/ast/import_test.cc
new file mode 100644
index 0000000..48f5a77
--- /dev/null
+++ b/src/ast/import_test.cc
@@ -0,0 +1,91 @@
+// Copyright 2020 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/ast/import.h"
+
+#include <sstream>
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using ImportTest = testing::Test;
+
+TEST_F(ImportTest, Creation) {
+ Import i("GLSL.std.430", "std::glsl");
+
+ EXPECT_EQ(i.path(), "GLSL.std.430");
+ EXPECT_EQ(i.name(), "std::glsl");
+ EXPECT_EQ(i.line(), 0);
+ EXPECT_EQ(i.column(), 0);
+}
+
+TEST_F(ImportTest, CreationWithSource) {
+ Source s{27, 4};
+ Import i(s, "GLSL.std.430", "std::glsl");
+
+ EXPECT_EQ(i.path(), "GLSL.std.430");
+ EXPECT_EQ(i.name(), "std::glsl");
+ EXPECT_EQ(i.line(), 27);
+ EXPECT_EQ(i.column(), 4);
+}
+
+TEST_F(ImportTest, CreationEmpty) {
+ Source s{27, 4};
+ Import i;
+ i.set_source(s);
+ i.set_path("GLSL.std.430");
+ i.set_name("std::glsl");
+
+ EXPECT_EQ(i.path(), "GLSL.std.430");
+ EXPECT_EQ(i.name(), "std::glsl");
+ EXPECT_EQ(i.line(), 27);
+ EXPECT_EQ(i.column(), 4);
+}
+
+TEST_F(ImportTest, to_str) {
+ Import i{"GLSL.std.430", "std::glsl"};
+ std::ostringstream out;
+ i.to_str(out, 2);
+ EXPECT_EQ(out.str(), " Import{\"GLSL.std.430\" as std::glsl}\n");
+}
+
+TEST_F(ImportTest, IsValid) {
+ Import i{"GLSL.std.430", "std::glsl"};
+ EXPECT_TRUE(i.IsValid());
+}
+
+TEST_F(ImportTest, IsValid_MissingPath) {
+ Import i{"", "std::glsl"};
+ EXPECT_FALSE(i.IsValid());
+}
+
+TEST_F(ImportTest, IsValid_MissingName) {
+ Import i{"GLSL.std.430", ""};
+ EXPECT_FALSE(i.IsValid());
+}
+
+TEST_F(ImportTest, IsValid_MissingBoth) {
+ Import i;
+ EXPECT_FALSE(i.IsValid());
+}
+
+TEST_F(ImportTest, IsValid_InvalidEndingCharacter) {
+ Import i{"GLSL.std.430", "std::glsl::"};
+ EXPECT_FALSE(i.IsValid());
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/initializer_expression.cc b/src/ast/initializer_expression.cc
new file mode 100644
index 0000000..f28f3c3
--- /dev/null
+++ b/src/ast/initializer_expression.cc
@@ -0,0 +1,43 @@
+// Copyright 2020 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/ast/initializer_expression.h"
+
+#include <assert.h>
+
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/type_initializer_expression.h"
+
+namespace tint {
+namespace ast {
+
+InitializerExpression::InitializerExpression() = default;
+
+InitializerExpression::~InitializerExpression() = default;
+
+InitializerExpression::InitializerExpression(const Source& source)
+ : Expression(source) {}
+
+ConstInitializerExpression* InitializerExpression::AsConstInitializer() {
+ assert(IsConstInitializer());
+ return static_cast<ConstInitializerExpression*>(this);
+}
+
+TypeInitializerExpression* InitializerExpression::AsTypeInitializer() {
+ assert(IsTypeInitializer());
+ return static_cast<TypeInitializerExpression*>(this);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/initializer_expression.h b/src/ast/initializer_expression.h
new file mode 100644
index 0000000..0067c20
--- /dev/null
+++ b/src/ast/initializer_expression.h
@@ -0,0 +1,60 @@
+// Copyright 2020 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_AST_INITIALIZER_EXPRESSION_H_
+#define SRC_AST_INITIALIZER_EXPRESSION_H_
+
+#include "src/ast/expression.h"
+
+namespace tint {
+namespace ast {
+
+class ConstInitializerExpression;
+class TypeInitializerExpression;
+
+/// Base class for initializer style expressions
+class InitializerExpression : public Expression {
+ public:
+ ~InitializerExpression() override;
+
+ /// @returns true if this is an initializer expression
+ bool IsInitializer() const override { return true; }
+
+ /// @returns true if this is a constant initializer
+ virtual bool IsConstInitializer() const { return false; }
+ /// @returns true if this is a type initializer
+ virtual bool IsTypeInitializer() const { return false; }
+
+ /// @returns this as a const initializer expression
+ ConstInitializerExpression* AsConstInitializer();
+ /// @returns this as a type initializer expression
+ TypeInitializerExpression* AsTypeInitializer();
+
+ protected:
+ /// Constructor
+ InitializerExpression();
+ /// Constructor
+ /// @param source the initializer source
+ explicit InitializerExpression(const Source& source);
+ /// Move constructor
+ InitializerExpression(InitializerExpression&&) = default;
+
+ private:
+ InitializerExpression(const InitializerExpression&) = delete;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_INITIALIZER_EXPRESSION_H_
diff --git a/src/ast/int_literal.cc b/src/ast/int_literal.cc
new file mode 100644
index 0000000..99e476c
--- /dev/null
+++ b/src/ast/int_literal.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 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/ast/int_literal.h"
+
+namespace tint {
+namespace ast {
+
+IntLiteral::IntLiteral(int32_t value) : value_(value) {}
+
+IntLiteral::~IntLiteral() = default;
+
+std::string IntLiteral::to_str() const {
+ return std::to_string(value_);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/int_literal.h b/src/ast/int_literal.h
new file mode 100644
index 0000000..b96e3b9
--- /dev/null
+++ b/src/ast/int_literal.h
@@ -0,0 +1,49 @@
+// Copyright 2020 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_AST_INT_LITERAL_H_
+#define SRC_AST_INT_LITERAL_H_
+
+#include <string>
+
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// A int literal
+class IntLiteral : public Literal {
+ public:
+ /// Constructor
+ /// @param value the int literals value
+ explicit IntLiteral(int32_t value);
+ ~IntLiteral() override;
+
+ /// @returns true if this is a int literal
+ bool IsInt() const override { return true; }
+
+ /// @returns the int literal value
+ int32_t value() const { return value_; }
+
+ /// @returns the literal as a string
+ std::string to_str() const override;
+
+ private:
+ int32_t value_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_INT_LITERAL_H_
diff --git a/src/ast/int_literal_test.cc b/src/ast/int_literal_test.cc
new file mode 100644
index 0000000..7b7fe86
--- /dev/null
+++ b/src/ast/int_literal_test.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 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/ast/int_literal.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using IntLiteralTest = testing::Test;
+
+TEST_F(IntLiteralTest, Value) {
+ IntLiteral i{47};
+ ASSERT_TRUE(i.IsInt());
+ EXPECT_EQ(i.value(), 47);
+}
+
+TEST_F(IntLiteralTest, Is) {
+ IntLiteral i{42};
+ EXPECT_FALSE(i.IsBool());
+ EXPECT_TRUE(i.IsInt());
+ EXPECT_FALSE(i.IsFloat());
+ EXPECT_FALSE(i.IsUint());
+}
+
+TEST_F(IntLiteralTest, ToStr) {
+ IntLiteral i{-42};
+
+ EXPECT_EQ(i.to_str(), "-42");
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/kill_statement.cc b/src/ast/kill_statement.cc
new file mode 100644
index 0000000..70581a7
--- /dev/null
+++ b/src/ast/kill_statement.cc
@@ -0,0 +1,36 @@
+// Copyright 2020 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/ast/kill_statement.h"
+
+namespace tint {
+namespace ast {
+
+KillStatement::KillStatement() : Statement() {}
+
+KillStatement::KillStatement(const Source& source) : Statement(source) {}
+
+KillStatement::~KillStatement() = default;
+
+bool KillStatement::IsValid() const {
+ return true;
+}
+
+void KillStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Kill{}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/kill_statement.h b/src/ast/kill_statement.h
new file mode 100644
index 0000000..15bf061
--- /dev/null
+++ b/src/ast/kill_statement.h
@@ -0,0 +1,53 @@
+// Copyright 2020 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_AST_KILL_STATEMENT_H_
+#define SRC_AST_KILL_STATEMENT_H_
+
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// A kill statement
+class KillStatement : public Statement {
+ public:
+ /// Constructor
+ KillStatement();
+ /// Constructor
+ /// @param source the initializer source
+ explicit KillStatement(const Source& source);
+ /// Move constructor
+ KillStatement(KillStatement&&) = default;
+ ~KillStatement() override;
+
+ /// @returns true if this is a kill statement
+ bool IsKill() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ KillStatement(const KillStatement&) = delete;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_KILL_STATEMENT_H_
diff --git a/src/ast/literal.cc b/src/ast/literal.cc
new file mode 100644
index 0000000..055d33d
--- /dev/null
+++ b/src/ast/literal.cc
@@ -0,0 +1,52 @@
+// Copyright 2020 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/ast/literal.h"
+
+#include <assert.h>
+
+#include "src/ast/bool_literal.h"
+#include "src/ast/float_literal.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/uint_literal.h"
+
+namespace tint {
+namespace ast {
+
+Literal::Literal() = default;
+
+Literal::~Literal() = default;
+
+BoolLiteral* Literal::AsBool() {
+ assert(IsBool());
+ return static_cast<BoolLiteral*>(this);
+}
+
+FloatLiteral* Literal::AsFloat() {
+ assert(IsFloat());
+ return static_cast<FloatLiteral*>(this);
+}
+
+IntLiteral* Literal::AsInt() {
+ assert(IsInt());
+ return static_cast<IntLiteral*>(this);
+}
+
+UintLiteral* Literal::AsUint() {
+ assert(IsUint());
+ return static_cast<UintLiteral*>(this);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/literal.h b/src/ast/literal.h
new file mode 100644
index 0000000..af8b831
--- /dev/null
+++ b/src/ast/literal.h
@@ -0,0 +1,62 @@
+// Copyright 2020 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_AST_LITERAL_H_
+#define SRC_AST_LITERAL_H_
+
+#include <string>
+
+namespace tint {
+namespace ast {
+
+class BoolLiteral;
+class FloatLiteral;
+class IntLiteral;
+class UintLiteral;
+
+/// Base class for a literal value
+class Literal {
+ public:
+ virtual ~Literal();
+
+ /// @returns true if this is a bool literal
+ virtual bool IsBool() const { return false; }
+ /// @returns true if this is a float literal
+ virtual bool IsFloat() const { return false; }
+ /// @returns true if this is a signed int literal
+ virtual bool IsInt() const { return false; }
+ /// @returns true if this is a unsigned int literal
+ virtual bool IsUint() const { return false; }
+
+ /// @returns the literal as a boolean literal
+ BoolLiteral* AsBool();
+ /// @returns the literal as a float literal
+ FloatLiteral* AsFloat();
+ /// @returns the literal as a int literal
+ IntLiteral* AsInt();
+ /// @returns the literal as a unsigned int literal
+ UintLiteral* AsUint();
+
+ /// @returns the literal as a string
+ virtual std::string to_str() const = 0;
+
+ protected:
+ /// Constructor
+ Literal();
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_LITERAL_H_
diff --git a/src/ast/location_decoration.cc b/src/ast/location_decoration.cc
new file mode 100644
index 0000000..45bec56
--- /dev/null
+++ b/src/ast/location_decoration.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 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/ast/location_decoration.h"
+
+namespace tint {
+namespace ast {
+
+LocationDecoration::LocationDecoration(size_t val) : value_(val) {}
+
+LocationDecoration::~LocationDecoration() = default;
+
+void LocationDecoration::to_str(std::ostream& out) const {
+ out << "location " << value_;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/location_decoration.h b/src/ast/location_decoration.h
new file mode 100644
index 0000000..69b61dc
--- /dev/null
+++ b/src/ast/location_decoration.h
@@ -0,0 +1,50 @@
+// Copyright 2020 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_AST_LOCATION_DECORATION_H_
+#define SRC_AST_LOCATION_DECORATION_H_
+
+#include <stddef.h>
+
+#include "src/ast/variable_decoration.h"
+
+namespace tint {
+namespace ast {
+
+/// A location decoration
+class LocationDecoration : public VariableDecoration {
+ public:
+ /// constructor
+ /// @param value the location value
+ explicit LocationDecoration(size_t value);
+ ~LocationDecoration() override;
+
+ /// @returns true if this is a location decoration
+ bool IsLocation() const override { return true; }
+
+ /// @returns the location value
+ size_t value() const { return value_; }
+
+ /// Outputs the decoration to the given stream
+ /// @param out the stream to output too
+ void to_str(std::ostream& out) const override;
+
+ private:
+ size_t value_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_LOCATION_DECORATION_H_
diff --git a/src/ast/location_decoration_test.cc b/src/ast/location_decoration_test.cc
new file mode 100644
index 0000000..7858193
--- /dev/null
+++ b/src/ast/location_decoration_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 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/ast/location_decoration.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using LocationDecorationTest = testing::Test;
+
+TEST_F(LocationDecorationTest, Creation) {
+ LocationDecoration d{2};
+ EXPECT_EQ(2, d.value());
+}
+
+TEST_F(LocationDecorationTest, Is) {
+ LocationDecoration d{2};
+ EXPECT_FALSE(d.IsBinding());
+ EXPECT_FALSE(d.IsBuiltin());
+ EXPECT_TRUE(d.IsLocation());
+ EXPECT_FALSE(d.IsSet());
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/loop_statement.cc b/src/ast/loop_statement.cc
new file mode 100644
index 0000000..b0e645b
--- /dev/null
+++ b/src/ast/loop_statement.cc
@@ -0,0 +1,58 @@
+// Copyright 2020 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/ast/loop_statement.h"
+
+namespace tint {
+namespace ast {
+
+LoopStatement::LoopStatement(std::vector<std::unique_ptr<Statement>> body,
+ std::vector<std::unique_ptr<Statement>> continuing)
+ : Statement(), body_(std::move(body)), continuing_(std::move(continuing)) {}
+
+LoopStatement::LoopStatement(const Source& source,
+ std::vector<std::unique_ptr<Statement>> body,
+ std::vector<std::unique_ptr<Statement>> continuing)
+ : Statement(source),
+ body_(std::move(body)),
+ continuing_(std::move(continuing)) {}
+
+LoopStatement::~LoopStatement() = default;
+
+bool LoopStatement::IsValid() const {
+ return true;
+}
+
+void LoopStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Loop{" << std::endl;
+
+ for (const auto& stmt : body_)
+ stmt->to_str(out, indent + 2);
+
+ make_indent(out, indent + 2);
+ out << "continuing {" << std::endl;
+
+ for (const auto& stmt : continuing_)
+ stmt->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << "}" << std::endl;
+
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/loop_statement.h b/src/ast/loop_statement.h
new file mode 100644
index 0000000..99acd96
--- /dev/null
+++ b/src/ast/loop_statement.h
@@ -0,0 +1,85 @@
+// Copyright 2020 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_AST_LOOP_STATEMENT_H_
+#define SRC_AST_LOOP_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// A loop statement
+class LoopStatement : public Statement {
+ public:
+ /// Constructor
+ /// @param body the body statements
+ /// @param continuing the continuing statements
+ LoopStatement(std::vector<std::unique_ptr<Statement>> body,
+ std::vector<std::unique_ptr<Statement>> continuing);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param body the body statements
+ /// @param continuing the continuing statements
+ LoopStatement(const Source& source,
+ std::vector<std::unique_ptr<Statement>> body,
+ std::vector<std::unique_ptr<Statement>> continuing);
+ /// Move constructor
+ LoopStatement(LoopStatement&&) = default;
+ ~LoopStatement() override;
+
+ /// Sets the body statements
+ /// @param body the body statements
+ void set_body(std::vector<std::unique_ptr<Statement>> body) {
+ body_ = std::move(body);
+ }
+ /// @returns the body statements
+ const std::vector<std::unique_ptr<Statement>>& body() const { return body_; }
+
+ /// Sets the continuing statements
+ /// @param continuing the continuing statements
+ void set_continuing(std::vector<std::unique_ptr<Statement>> continuing) {
+ continuing_ = std::move(continuing);
+ }
+ /// @returns the continuing statements
+ const std::vector<std::unique_ptr<Statement>>& continuing() const {
+ return continuing_;
+ }
+
+ /// @returns true if this is a loop statement
+ bool IsLoop() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ LoopStatement(const LoopStatement&) = delete;
+
+ std::vector<std::unique_ptr<Statement>> body_;
+ std::vector<std::unique_ptr<Statement>> continuing_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_LOOP_STATEMENT_H_
diff --git a/src/ast/member_accessor_expression.cc b/src/ast/member_accessor_expression.cc
new file mode 100644
index 0000000..24f43ba
--- /dev/null
+++ b/src/ast/member_accessor_expression.cc
@@ -0,0 +1,49 @@
+// Copyright 2020 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/ast/member_accessor_expression.h"
+
+namespace tint {
+namespace ast {
+
+MemberAccessorExpression::MemberAccessorExpression(
+ std::unique_ptr<Expression> structure,
+ std::unique_ptr<IdentifierExpression> member)
+ : Expression(), struct_(std::move(structure)), member_(std::move(member)) {}
+
+MemberAccessorExpression::MemberAccessorExpression(
+ const Source& source,
+ std::unique_ptr<Expression> structure,
+ std::unique_ptr<IdentifierExpression> member)
+ : Expression(source),
+ struct_(std::move(structure)),
+ member_(std::move(member)) {}
+
+MemberAccessorExpression::~MemberAccessorExpression() = default;
+
+bool MemberAccessorExpression::IsValid() const {
+ return struct_ != nullptr && member_ != nullptr;
+}
+
+void MemberAccessorExpression::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "MemberAccessor{" << std::endl;
+ struct_->to_str(out, indent + 2);
+ member_->to_str(out, indent + 2);
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/member_accessor_expression.h b/src/ast/member_accessor_expression.h
new file mode 100644
index 0000000..ff24bad
--- /dev/null
+++ b/src/ast/member_accessor_expression.h
@@ -0,0 +1,85 @@
+// Copyright 2020 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_AST_MEMBER_ACCESSOR_EXPRESSION_H_
+#define SRC_AST_MEMBER_ACCESSOR_EXPRESSION_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// A member accessor expression
+class MemberAccessorExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param structure the structure
+ /// @param member the member
+ MemberAccessorExpression(std::unique_ptr<Expression> structure,
+ std::unique_ptr<IdentifierExpression> member);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param structure the structure
+ /// @param member the member
+ MemberAccessorExpression(const Source& source,
+ std::unique_ptr<Expression> structure,
+ std::unique_ptr<IdentifierExpression> member);
+ /// Move constructor
+ MemberAccessorExpression(MemberAccessorExpression&&) = default;
+ ~MemberAccessorExpression() override;
+
+ /// Sets the structure
+ /// @param structure the structure
+ void set_structure(std::unique_ptr<Expression> structure) {
+ struct_ = std::move(structure);
+ }
+ /// @returns the structure
+ Expression* structure() const { return struct_.get(); }
+
+ /// Sets the member
+ /// @param member the member
+ void set_member(std::unique_ptr<IdentifierExpression> member) {
+ member_ = std::move(member);
+ }
+ /// @returns the member expression
+ IdentifierExpression* member() const { return member_.get(); }
+
+ /// @returns true if this is a member accessor expression
+ bool IsMemberAccessor() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ MemberAccessorExpression(const MemberAccessorExpression&) = delete;
+
+ std::unique_ptr<Expression> struct_;
+ std::unique_ptr<IdentifierExpression> member_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_MEMBER_ACCESSOR_EXPRESSION_H_
diff --git a/src/ast/module.cc b/src/ast/module.cc
new file mode 100644
index 0000000..e5c5015
--- /dev/null
+++ b/src/ast/module.cc
@@ -0,0 +1,61 @@
+// Copyright 2020 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/ast/module.h"
+
+#include <sstream>
+
+namespace tint {
+namespace ast {
+
+const Import* Module::FindImportByName(const std::string& name) {
+ for (const auto& import : imports_) {
+ if (import->name() == name)
+ return import.get();
+ }
+ return nullptr;
+}
+
+bool Module::IsValid() const {
+ for (const auto& import : imports_) {
+ if (!import->IsValid())
+ return false;
+ }
+ return true;
+}
+
+std::string Module::to_str() const {
+ std::ostringstream out;
+
+ for (const auto& import : imports_) {
+ import->to_str(out, 0);
+ }
+ for (const auto& var : global_variables_) {
+ var->to_str(out, 0);
+ }
+ for (const auto& ep : entry_points_) {
+ ep->to_str(out, 0);
+ }
+ for (const auto& alias : alias_types_) {
+ out << alias->name() << " -> " << alias->type()->type_name() << std::endl;
+ }
+ for (const auto& func : functions_) {
+ func->to_str(out, 0);
+ }
+
+ return out.str();
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/module.h b/src/ast/module.h
new file mode 100644
index 0000000..e0d1a7e
--- /dev/null
+++ b/src/ast/module.h
@@ -0,0 +1,110 @@
+// Copyright 2020 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_AST_MODULE_H_
+#define SRC_AST_MODULE_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/ast/entry_point.h"
+#include "src/ast/function.h"
+#include "src/ast/import.h"
+#include "src/ast/type/alias_type.h"
+#include "src/ast/variable.h"
+
+namespace tint {
+namespace ast {
+
+/// Represents all the source in a given program.
+class Module {
+ public:
+ Module() = default;
+ /// Move constructor
+ Module(Module&&) = default;
+ ~Module() = default;
+
+ /// Add the given import to the module
+ /// @param import The import to add.
+ void AddImport(std::unique_ptr<Import> import) {
+ imports_.push_back(std::move(import));
+ }
+ /// @returns the imports for this module
+ const std::vector<std::unique_ptr<Import>>& imports() { return imports_; }
+ /// Find the import of the given name
+ /// @param name The import name to search for
+ /// @returns the import with the given name if found, nullptr otherwise.
+ const Import* FindImportByName(const std::string& name);
+
+ /// Add a global variable to the module
+ /// @param var the variable to add
+ void AddGlobalVariable(std::unique_ptr<Variable> var) {
+ global_variables_.push_back(std::move(var));
+ }
+ /// @returns the global variables for the module
+ const std::vector<std::unique_ptr<Variable>>& global_variables() const {
+ return global_variables_;
+ }
+
+ /// Adds an entry point to the module
+ /// @param ep the entry point to add
+ void AddEntryPoint(std::unique_ptr<EntryPoint> ep) {
+ entry_points_.push_back(std::move(ep));
+ }
+ /// @returns the entry points in the module
+ const std::vector<std::unique_ptr<EntryPoint>>& entry_points() const {
+ return entry_points_;
+ }
+
+ /// Adds a type alias to the module
+ /// @param type the alias to add
+ void AddAliasType(type::AliasType* type) { alias_types_.push_back(type); }
+ /// @returns the alias types in the module
+ const std::vector<type::AliasType*>& alias_types() const {
+ return alias_types_;
+ }
+
+ /// Adds a function to the module
+ /// @param func the function
+ void AddFunction(std::unique_ptr<Function> func) {
+ functions_.push_back(std::move(func));
+ }
+ /// @returns the modules functions
+ const std::vector<std::unique_ptr<Function>>& functions() const {
+ return functions_;
+ }
+
+ /// @returns true if all required fields in the AST are present.
+ bool IsValid() const;
+
+ /// @returns a string representation of the module
+ std::string to_str() const;
+
+ private:
+ Module(const Module&) = delete;
+
+ std::vector<std::unique_ptr<Import>> imports_;
+ std::vector<std::unique_ptr<Variable>> global_variables_;
+ std::vector<std::unique_ptr<EntryPoint>> entry_points_;
+ // The alias types are owned by the type manager
+ std::vector<type::AliasType*> alias_types_;
+ std::vector<std::unique_ptr<Function>> functions_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_MODULE_H_
diff --git a/src/ast/module_test.cc b/src/ast/module_test.cc
new file mode 100644
index 0000000..2cf1626
--- /dev/null
+++ b/src/ast/module_test.cc
@@ -0,0 +1,78 @@
+// Copyright 2020 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/ast/module.h"
+
+#include <utility>
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using ModuleTest = testing::Test;
+
+TEST_F(ModuleTest, Creation) {
+ Module m;
+
+ EXPECT_EQ(m.imports().size(), 0);
+}
+
+TEST_F(ModuleTest, Imports) {
+ Module m;
+
+ m.AddImport(std::make_unique<Import>("GLSL.std.430", "std::glsl"));
+ m.AddImport(std::make_unique<Import>("OpenCL.debug.100", "std::debug"));
+
+ EXPECT_EQ(2, m.imports().size());
+ EXPECT_EQ("std::glsl", m.imports()[0]->name());
+}
+
+TEST_F(ModuleTest, LookupImport) {
+ Module m;
+
+ auto i = std::make_unique<Import>("GLSL.std.430", "std::glsl");
+ m.AddImport(std::move(i));
+ m.AddImport(std::make_unique<Import>("OpenCL.debug.100", "std::debug"));
+
+ auto import = m.FindImportByName("std::glsl");
+ ASSERT_NE(nullptr, import);
+ EXPECT_EQ(import->path(), "GLSL.std.430");
+ EXPECT_EQ(import->name(), "std::glsl");
+}
+
+TEST_F(ModuleTest, LookupImportMissing) {
+ Module m;
+ EXPECT_EQ(nullptr, m.FindImportByName("Missing"));
+}
+
+TEST_F(ModuleTest, IsValid_Empty) {
+ Module m;
+ EXPECT_TRUE(m.IsValid());
+}
+
+TEST_F(ModuleTest, IsValid_InvalidImport) {
+ Module m;
+ m.AddImport(std::make_unique<Import>());
+ EXPECT_FALSE(m.IsValid());
+}
+
+TEST_F(ModuleTest, IsValid_ValidImport) {
+ Module m;
+ m.AddImport(std::make_unique<Import>("GLSL.std.430", "std::glsl"));
+ EXPECT_TRUE(m.IsValid());
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/node.cc b/src/ast/node.cc
new file mode 100644
index 0000000..8870db0
--- /dev/null
+++ b/src/ast/node.cc
@@ -0,0 +1,40 @@
+// Copyright 2020 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/ast/node.h"
+
+#include <sstream>
+
+namespace tint {
+namespace ast {
+
+Node::Node() = default;
+
+Node::Node(const Source& source) : source_(source) {}
+
+Node::~Node() = default;
+
+void Node::make_indent(std::ostream& out, size_t indent) const {
+ for (size_t i = 0; i < indent; ++i)
+ out << " ";
+}
+
+std::string Node::str() const {
+ std::ostringstream out;
+ to_str(out, 0);
+ return out.str();
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/node.h b/src/ast/node.h
new file mode 100644
index 0000000..97c3bcf
--- /dev/null
+++ b/src/ast/node.h
@@ -0,0 +1,77 @@
+// Copyright 2020 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_AST_NODE_H_
+#define SRC_AST_NODE_H_
+
+#include <ostream>
+#include <string>
+
+#include "src/source.h"
+
+namespace tint {
+namespace ast {
+
+/// AST base class node
+class Node {
+ public:
+ virtual ~Node();
+
+ /// @returns the node source data
+ const Source& source() const { return source_; }
+ /// Sets the source data
+ /// @param source the source data
+ void set_source(const Source& source) { source_ = source; }
+
+ /// @returns the line the node was declared on
+ size_t line() const { return source_.line; }
+ /// @returns the column the node was declared on
+ size_t column() const { return source_.column; }
+
+ /// @returns true if the node is valid
+ virtual bool IsValid() const = 0;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ virtual void to_str(std::ostream& out, size_t indent) const = 0;
+
+ /// Convenience wrapper around the |to_str| method.
+ /// @returns the node as a string
+ std::string str() const;
+
+ protected:
+ /// Create a new node
+ Node();
+ /// Create a new node
+ /// @param source The input source for the node
+ explicit Node(const Source& source);
+ /// Move constructor
+ Node(Node&&) = default;
+
+ /// Writes indent into stream
+ /// @param out the stream to write to
+ /// @param indent the number of spaces to write
+ void make_indent(std::ostream& out, size_t indent) const;
+
+ private:
+ Node(const Node&) = delete;
+
+ Source source_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_NODE_H_
diff --git a/src/ast/nop_statement.cc b/src/ast/nop_statement.cc
new file mode 100644
index 0000000..b325bb3
--- /dev/null
+++ b/src/ast/nop_statement.cc
@@ -0,0 +1,36 @@
+// Copyright 2020 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/ast/nop_statement.h"
+
+namespace tint {
+namespace ast {
+
+NopStatement::NopStatement() : Statement() {}
+
+NopStatement::NopStatement(const Source& source) : Statement(source) {}
+
+NopStatement::~NopStatement() = default;
+
+bool NopStatement::IsValid() const {
+ return true;
+}
+
+void NopStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Nop{}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/nop_statement.h b/src/ast/nop_statement.h
new file mode 100644
index 0000000..6585728
--- /dev/null
+++ b/src/ast/nop_statement.h
@@ -0,0 +1,53 @@
+// Copyright 2020 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_AST_NOP_STATEMENT_H_
+#define SRC_AST_NOP_STATEMENT_H_
+
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// A nop statement
+class NopStatement : public Statement {
+ public:
+ /// Constructor
+ NopStatement();
+ /// Constructor
+ /// @param source the initializer source
+ explicit NopStatement(const Source& source);
+ /// Move constructor
+ NopStatement(NopStatement&&) = default;
+ ~NopStatement() override;
+
+ /// @returns true if this is a nop statement
+ bool IsNop() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ NopStatement(const NopStatement&) = delete;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_NOP_STATEMENT_H_
diff --git a/src/ast/pipeline_stage.cc b/src/ast/pipeline_stage.cc
new file mode 100644
index 0000000..ec2682b
--- /dev/null
+++ b/src/ast/pipeline_stage.cc
@@ -0,0 +1,43 @@
+// Copyright 2020 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/ast/pipeline_stage.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, PipelineStage stage) {
+ switch (stage) {
+ case PipelineStage::kNone: {
+ out << "none";
+ break;
+ }
+ case PipelineStage::kVertex: {
+ out << "vertex";
+ break;
+ }
+ case PipelineStage::kFragment: {
+ out << "fragment";
+ break;
+ }
+ case PipelineStage::kCompute: {
+ out << "compute";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/pipeline_stage.h b/src/ast/pipeline_stage.h
new file mode 100644
index 0000000..3733c8c
--- /dev/null
+++ b/src/ast/pipeline_stage.h
@@ -0,0 +1,31 @@
+// Copyright 2020 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_AST_PIPELINE_STAGE_H_
+#define SRC_AST_PIPELINE_STAGE_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// The pipeline stage
+enum class PipelineStage { kNone = -1, kVertex, kFragment, kCompute };
+
+std::ostream& operator<<(std::ostream& out, PipelineStage stage);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_PIPELINE_STAGE_H_
diff --git a/src/ast/regardless_statement.cc b/src/ast/regardless_statement.cc
new file mode 100644
index 0000000..2ad7bb4
--- /dev/null
+++ b/src/ast/regardless_statement.cc
@@ -0,0 +1,58 @@
+// Copyright 2020 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/ast/regardless_statement.h"
+
+namespace tint {
+namespace ast {
+
+RegardlessStatement::RegardlessStatement(
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(), condition_(std::move(condition)), body_(std::move(body)) {}
+
+RegardlessStatement::RegardlessStatement(
+ const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(source),
+ condition_(std::move(condition)),
+ body_(std::move(body)) {}
+
+RegardlessStatement::~RegardlessStatement() = default;
+
+bool RegardlessStatement::IsValid() const {
+ return condition_ != nullptr;
+}
+
+void RegardlessStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Regardless{" << std::endl;
+
+ condition_->to_str(out, indent + 2);
+ make_indent(out, indent);
+ out << "{" << std::endl;
+
+ for (const auto& stmt : body_)
+ stmt->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << "}";
+
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/regardless_statement.h b/src/ast/regardless_statement.h
new file mode 100644
index 0000000..54e9c60
--- /dev/null
+++ b/src/ast/regardless_statement.h
@@ -0,0 +1,84 @@
+// Copyright 2020 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_AST_REGARDLESS_STATEMENT_H_
+#define SRC_AST_REGARDLESS_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// A regardless statement
+class RegardlessStatement : public Statement {
+ public:
+ /// Constructor
+ /// @param condition the condition expression
+ /// @param body the body statements
+ RegardlessStatement(std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param condition the condition expression
+ /// @param body the body statements
+ RegardlessStatement(const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Move constructor
+ RegardlessStatement(RegardlessStatement&&) = default;
+ ~RegardlessStatement() override;
+
+ /// Sets the condition expression
+ /// @param condition the condition expression
+ void set_condition(std::unique_ptr<Expression> condition) {
+ condition_ = std::move(condition);
+ }
+ /// @returns the condition statements
+ Expression* condition() const { return condition_.get(); }
+
+ /// Sets the body statements
+ /// @param body the body statements
+ void set_body(std::vector<std::unique_ptr<Statement>> body) {
+ body_ = std::move(body);
+ }
+ /// @returns the body statements
+ const std::vector<std::unique_ptr<Statement>>& body() const { return body_; }
+
+ /// @returns true if this is an regardless statement
+ bool IsRegardless() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ RegardlessStatement(const RegardlessStatement&) = delete;
+
+ std::unique_ptr<Expression> condition_;
+ std::vector<std::unique_ptr<Statement>> body_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_REGARDLESS_STATEMENT_H_
diff --git a/src/ast/relational_expression.cc b/src/ast/relational_expression.cc
new file mode 100644
index 0000000..794df41
--- /dev/null
+++ b/src/ast/relational_expression.cc
@@ -0,0 +1,55 @@
+// Copyright 2020 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/ast/relational_expression.h"
+
+namespace tint {
+namespace ast {
+
+RelationalExpression::RelationalExpression(Relation relation,
+ std::unique_ptr<Expression> lhs,
+ std::unique_ptr<Expression> rhs)
+ : Expression(),
+ relation_(relation),
+ lhs_(std::move(lhs)),
+ rhs_(std::move(rhs)) {}
+
+RelationalExpression::RelationalExpression(const Source& source,
+ Relation relation,
+ std::unique_ptr<Expression> lhs,
+ std::unique_ptr<Expression> rhs)
+ : Expression(source),
+ relation_(relation),
+ lhs_(std::move(lhs)),
+ rhs_(std::move(rhs)) {}
+
+RelationalExpression::~RelationalExpression() = default;
+
+bool RelationalExpression::IsValid() const {
+ return relation_ != Relation::kNone && lhs_ != nullptr && rhs_ != nullptr;
+}
+
+void RelationalExpression::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << relation_ << "(" << std::endl;
+ lhs_->to_str(out, indent + 2);
+ out << std::endl;
+ rhs_->to_str(out, indent + 2);
+ out << std::endl;
+ make_indent(out, indent);
+ out << ")" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/relational_expression.h b/src/ast/relational_expression.h
new file mode 100644
index 0000000..1404353
--- /dev/null
+++ b/src/ast/relational_expression.h
@@ -0,0 +1,220 @@
+// Copyright 2020 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_AST_RELATIONAL_EXPRESSION_H_
+#define SRC_AST_RELATIONAL_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// The relation type
+enum class Relation {
+ kNone = 0,
+ kAnd,
+ kOr,
+ kXor,
+ kLogicalAnd,
+ kLogicalOr,
+ kEqual,
+ kNotEqual,
+ kLessThan,
+ kGreaterThan,
+ kLessThanEqual,
+ kGreaterThanEqual,
+ kUnordGreaterThan,
+ kUnordGreaterThanEqual,
+ kUnordLessThan,
+ kUnordLessThanEqual,
+ kUnordEqual,
+ kUnordNotEqual,
+ kSignedGreaterThan,
+ kSignedGreaterThanEqual,
+ kSignedLessThan,
+ kSignedLessThanEqual,
+ kShiftLeft,
+ kShiftRight,
+ kShiftRightArith,
+ kAdd,
+ kSubtract,
+ kMultiply,
+ kDivide,
+ kModulo,
+};
+
+/// An xor expression
+class RelationalExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param relation the relation type
+ /// @param lhs the left side of the expression
+ /// @param rhs the right side of the expression
+ RelationalExpression(Relation relation,
+ std::unique_ptr<Expression> lhs,
+ std::unique_ptr<Expression> rhs);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param relation the relation type
+ /// @param lhs the left side of the expression
+ /// @param rhs the right side of the expression
+ RelationalExpression(const Source& source,
+ Relation relation,
+ std::unique_ptr<Expression> lhs,
+ std::unique_ptr<Expression> rhs);
+ /// Move constructor
+ RelationalExpression(RelationalExpression&&) = default;
+ ~RelationalExpression() override;
+
+ /// Sets the relation type
+ /// @param relation the relation type
+ void set_relation(Relation relation) { relation_ = relation; }
+ /// @returns the relation
+ Relation relation() const { return relation_; }
+
+ /// Sets the left side of the expression
+ /// @param lhs the left side to set
+ void set_lhs(std::unique_ptr<Expression> lhs) { lhs_ = std::move(lhs); }
+ /// @returns the left side expression
+ Expression* lhs() const { return lhs_.get(); }
+
+ /// Sets the right side of the expression
+ /// @param rhs the right side to set
+ void set_rhs(std::unique_ptr<Expression> rhs) { rhs_ = std::move(rhs); }
+ /// @returns the right side expression
+ Expression* rhs() const { return rhs_.get(); }
+
+ /// @returns true if this is a relational expression
+ bool IsRelational() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ RelationalExpression(const RelationalExpression&) = delete;
+
+ Relation relation_ = Relation::kNone;
+ std::unique_ptr<Expression> lhs_;
+ std::unique_ptr<Expression> rhs_;
+};
+
+inline std::ostream& operator<<(std::ostream& out, Relation relation) {
+ switch (relation) {
+ case Relation::kNone:
+ out << "none";
+ break;
+ case Relation::kAnd:
+ out << "and";
+ break;
+ case Relation::kOr:
+ out << "or";
+ break;
+ case Relation::kXor:
+ out << "xor";
+ break;
+ case Relation::kLogicalAnd:
+ out << "logical_and";
+ break;
+ case Relation::kLogicalOr:
+ out << "logical_or";
+ break;
+ case Relation::kEqual:
+ out << "equal";
+ break;
+ case Relation::kNotEqual:
+ out << "not_equal";
+ break;
+ case Relation::kLessThan:
+ out << "less_than";
+ break;
+ case Relation::kGreaterThan:
+ out << "greater_than";
+ break;
+ case Relation::kLessThanEqual:
+ out << "less_than_equal";
+ break;
+ case Relation::kGreaterThanEqual:
+ out << "greater_than_equal";
+ break;
+ case Relation::kUnordGreaterThan:
+ out << "unord_greater_than";
+ break;
+ case Relation::kUnordGreaterThanEqual:
+ out << "unord_greater_than_equal";
+ break;
+ case Relation::kUnordLessThan:
+ out << "unord_less_than";
+ break;
+ case Relation::kUnordLessThanEqual:
+ out << "unord_less_than_equal";
+ break;
+ case Relation::kUnordEqual:
+ out << "unord_equal";
+ break;
+ case Relation::kUnordNotEqual:
+ out << "unord_not_equal";
+ break;
+ case Relation::kSignedGreaterThan:
+ out << "signed_greateR_than";
+ break;
+ case Relation::kSignedGreaterThanEqual:
+ out << "signed_greater_than_equal";
+ break;
+ case Relation::kSignedLessThan:
+ out << "signed_less_than";
+ break;
+ case Relation::kSignedLessThanEqual:
+ out << "signed_less_than_equal";
+ break;
+ case Relation::kShiftLeft:
+ out << "shift_left";
+ break;
+ case Relation::kShiftRight:
+ out << "shift_right";
+ break;
+ case Relation::kShiftRightArith:
+ out << "shift_right_arith";
+ break;
+ case Relation::kAdd:
+ out << "add";
+ break;
+ case Relation::kSubtract:
+ out << "subtract";
+ break;
+ case Relation::kMultiply:
+ out << "multiply";
+ break;
+ case Relation::kDivide:
+ out << "divide";
+ break;
+ case Relation::kModulo:
+ out << "modulo";
+ break;
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_RELATIONAL_EXPRESSION_H_
diff --git a/src/ast/return_statement.cc b/src/ast/return_statement.cc
new file mode 100644
index 0000000..5a6dcc2
--- /dev/null
+++ b/src/ast/return_statement.cc
@@ -0,0 +1,48 @@
+// Copyright 2020 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/ast/return_statement.h"
+
+namespace tint {
+namespace ast {
+
+ReturnStatement::ReturnStatement() : Statement() {}
+
+ReturnStatement::ReturnStatement(std::unique_ptr<Expression> value)
+ : Statement(), value_(std::move(value)) {}
+
+ReturnStatement::ReturnStatement(const Source& source,
+ std::unique_ptr<Expression> value)
+ : Statement(source), value_(std::move(value)) {}
+
+ReturnStatement::~ReturnStatement() = default;
+
+bool ReturnStatement::IsValid() const {
+ return true;
+}
+
+void ReturnStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Return{";
+
+ if (value_) {
+ out << std::endl;
+ value_->to_str(out, indent);
+ make_indent(out, indent);
+ }
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/return_statement.h b/src/ast/return_statement.h
new file mode 100644
index 0000000..30ada9c
--- /dev/null
+++ b/src/ast/return_statement.h
@@ -0,0 +1,71 @@
+// Copyright 2020 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_AST_RETURN_STATEMENT_H_
+#define SRC_AST_RETURN_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// A return statement
+class ReturnStatement : public Statement {
+ public:
+ /// Constructor
+ ReturnStatement();
+ /// Constructor
+ /// @param value the return value
+ explicit ReturnStatement(std::unique_ptr<Expression> value);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param value the return value
+ ReturnStatement(const Source& source, std::unique_ptr<Expression> value);
+ /// Move constructor
+ ReturnStatement(ReturnStatement&&) = default;
+ ~ReturnStatement() override;
+
+ /// Sets the value
+ /// @param value the value
+ void set_value(std::unique_ptr<Expression> value) {
+ value_ = std::move(value);
+ }
+ /// @returns the value
+ Expression* value() const { return value_.get(); }
+
+ /// @returns true if this is a return statement
+ bool IsReturn() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ ReturnStatement(const ReturnStatement&) = delete;
+
+ std::unique_ptr<Expression> value_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_RETURN_STATEMENT_H_
diff --git a/src/ast/set_decoration.cc b/src/ast/set_decoration.cc
new file mode 100644
index 0000000..c27ee26
--- /dev/null
+++ b/src/ast/set_decoration.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 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/ast/set_decoration.h"
+
+namespace tint {
+namespace ast {
+
+SetDecoration::SetDecoration(size_t val) : value_(val) {}
+
+SetDecoration::~SetDecoration() = default;
+
+void SetDecoration::to_str(std::ostream& out) const {
+ out << "set " << value_;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/set_decoration.h b/src/ast/set_decoration.h
new file mode 100644
index 0000000..253e940
--- /dev/null
+++ b/src/ast/set_decoration.h
@@ -0,0 +1,50 @@
+// Copyright 2020 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_AST_SET_DECORATION_H_
+#define SRC_AST_SET_DECORATION_H_
+
+#include <stddef.h>
+
+#include "src/ast/variable_decoration.h"
+
+namespace tint {
+namespace ast {
+
+/// A set decoration
+class SetDecoration : public VariableDecoration {
+ public:
+ /// constructor
+ /// @param value the set value
+ explicit SetDecoration(size_t value);
+ ~SetDecoration() override;
+
+ /// @returns true if this is a set decoration
+ bool IsSet() const override { return true; }
+
+ /// @returns the set value
+ size_t value() const { return value_; }
+
+ /// Outputs the decoration to the given stream
+ /// @param out the stream to output too
+ void to_str(std::ostream& out) const override;
+
+ private:
+ size_t value_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_SET_DECORATION_H_
diff --git a/src/ast/set_decoration_test.cc b/src/ast/set_decoration_test.cc
new file mode 100644
index 0000000..8295e1f
--- /dev/null
+++ b/src/ast/set_decoration_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 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/ast/set_decoration.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using SetDecorationTest = testing::Test;
+
+TEST_F(SetDecorationTest, Creation) {
+ SetDecoration d{2};
+ EXPECT_EQ(2, d.value());
+}
+
+TEST_F(SetDecorationTest, Is) {
+ SetDecoration d{2};
+ EXPECT_FALSE(d.IsBinding());
+ EXPECT_FALSE(d.IsBuiltin());
+ EXPECT_FALSE(d.IsLocation());
+ EXPECT_TRUE(d.IsSet());
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/statement.cc b/src/ast/statement.cc
new file mode 100644
index 0000000..07a1f09
--- /dev/null
+++ b/src/ast/statement.cc
@@ -0,0 +1,120 @@
+// Copyright 2020 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/ast/statement.h"
+
+#include <assert.h>
+
+#include "src/ast/assignment_statement.h"
+#include "src/ast/break_statement.h"
+#include "src/ast/case_statement.h"
+#include "src/ast/continue_statement.h"
+#include "src/ast/else_statement.h"
+#include "src/ast/fallthrough_statement.h"
+#include "src/ast/if_statement.h"
+#include "src/ast/kill_statement.h"
+#include "src/ast/loop_statement.h"
+#include "src/ast/nop_statement.h"
+#include "src/ast/regardless_statement.h"
+#include "src/ast/return_statement.h"
+#include "src/ast/switch_statement.h"
+#include "src/ast/unless_statement.h"
+#include "src/ast/variable_statement.h"
+
+namespace tint {
+namespace ast {
+
+Statement::Statement() = default;
+
+Statement::Statement(const Source& source) : Node(source) {}
+
+Statement::~Statement() = default;
+
+AssignmentStatement* Statement::AsAssign() {
+ assert(IsAssign());
+ return static_cast<AssignmentStatement*>(this);
+}
+
+BreakStatement* Statement::AsBreak() {
+ assert(IsBreak());
+ return static_cast<BreakStatement*>(this);
+}
+
+CaseStatement* Statement::AsCase() {
+ assert(IsCase());
+ return static_cast<CaseStatement*>(this);
+}
+
+ContinueStatement* Statement::AsContinue() {
+ assert(IsContinue());
+ return static_cast<ContinueStatement*>(this);
+}
+
+ElseStatement* Statement::AsElse() {
+ assert(IsElse());
+ return static_cast<ElseStatement*>(this);
+}
+
+FallthroughStatement* Statement::AsFallthrough() {
+ assert(IsFallthrough());
+ return static_cast<FallthroughStatement*>(this);
+}
+
+IfStatement* Statement::AsIf() {
+ assert(IsIf());
+ return static_cast<IfStatement*>(this);
+}
+
+KillStatement* Statement::AsKill() {
+ assert(IsKill());
+ return static_cast<KillStatement*>(this);
+}
+
+LoopStatement* Statement::AsLoop() {
+ assert(IsLoop());
+ return static_cast<LoopStatement*>(this);
+}
+
+NopStatement* Statement::AsNop() {
+ assert(IsNop());
+ return static_cast<NopStatement*>(this);
+}
+
+RegardlessStatement* Statement::AsRegardless() {
+ assert(IsRegardless());
+ return static_cast<RegardlessStatement*>(this);
+}
+
+ReturnStatement* Statement::AsReturn() {
+ assert(IsReturn());
+ return static_cast<ReturnStatement*>(this);
+}
+
+SwitchStatement* Statement::AsSwitch() {
+ assert(IsSwitch());
+ return static_cast<SwitchStatement*>(this);
+}
+
+UnlessStatement* Statement::AsUnless() {
+ assert(IsUnless());
+ return static_cast<UnlessStatement*>(this);
+}
+
+VariableStatement* Statement::AsVariable() {
+ assert(IsVariable());
+ return static_cast<VariableStatement*>(this);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/statement.h b/src/ast/statement.h
new file mode 100644
index 0000000..112f580
--- /dev/null
+++ b/src/ast/statement.h
@@ -0,0 +1,122 @@
+// Copyright 2020 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_AST_STATEMENT_H_
+#define SRC_AST_STATEMENT_H_
+
+#include "src/ast/node.h"
+
+namespace tint {
+namespace ast {
+
+class AssignmentStatement;
+class BreakStatement;
+class CaseStatement;
+class ContinueStatement;
+class ElseStatement;
+class FallthroughStatement;
+class IfStatement;
+class KillStatement;
+class LoopStatement;
+class NopStatement;
+class RegardlessStatement;
+class ReturnStatement;
+class SwitchStatement;
+class UnlessStatement;
+class VariableStatement;
+
+/// Base statement class
+class Statement : public Node {
+ public:
+ ~Statement() override;
+
+ /// @returns true if this is an assign statement
+ virtual bool IsAssign() const { return false; }
+ /// @returns true if this is a break statement
+ virtual bool IsBreak() const { return false; }
+ /// @returns true if this is a case statement
+ virtual bool IsCase() const { return false; }
+ /// @returns true if this is a continue statement
+ virtual bool IsContinue() const { return false; }
+ /// @returns true if this is an else statement
+ virtual bool IsElse() const { return false; }
+ /// @returns true if this is a fallthrough statement
+ virtual bool IsFallthrough() const { return false; }
+ /// @returns true if this is an if statement
+ virtual bool IsIf() const { return false; }
+ /// @returns true if this is a kill statement
+ virtual bool IsKill() const { return false; }
+ /// @returns true if this is a loop statement
+ virtual bool IsLoop() const { return false; }
+ /// @returns true if this is a nop statement
+ virtual bool IsNop() const { return false; }
+ /// @returns true if this is an regardless statement
+ virtual bool IsRegardless() const { return false; }
+ /// @returns true if this is a return statement
+ virtual bool IsReturn() const { return false; }
+ /// @returns true if this is a switch statement
+ virtual bool IsSwitch() const { return false; }
+ /// @returns true if this is an unless statement
+ virtual bool IsUnless() const { return false; }
+ /// @returns true if this is an variable statement
+ virtual bool IsVariable() const { return false; }
+
+ /// @returns the statement as an assign statement
+ AssignmentStatement* AsAssign();
+ /// @returns the statement as a break statement
+ BreakStatement* AsBreak();
+ /// @returns the statement as a case statement
+ CaseStatement* AsCase();
+ /// @returns the statement as a continue statement
+ ContinueStatement* AsContinue();
+ /// @returns the statement as a else statement
+ ElseStatement* AsElse();
+ /// @returns the statement as a fallthrough statement
+ FallthroughStatement* AsFallthrough();
+ /// @returns the statement as a if statement
+ IfStatement* AsIf();
+ /// @returns the statement as a kill statement
+ KillStatement* AsKill();
+ /// @returns the statement as a loop statement
+ LoopStatement* AsLoop();
+ /// @returns the statement as a nop statement
+ NopStatement* AsNop();
+ /// @returns the statement as an regardless statement
+ RegardlessStatement* AsRegardless();
+ /// @returns the statement as a return statement
+ ReturnStatement* AsReturn();
+ /// @returns the statement as a switch statement
+ SwitchStatement* AsSwitch();
+ /// @returns the statement as an unless statement
+ UnlessStatement* AsUnless();
+ /// @returns the statement as an variable statement
+ VariableStatement* AsVariable();
+
+ protected:
+ /// Constructor
+ Statement();
+ /// Constructor
+ /// @param source the source of the expression
+ explicit Statement(const Source& source);
+ /// Move constructor
+ Statement(Statement&&) = default;
+
+ private:
+ Statement(const Statement&) = delete;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_STATEMENT_H_
diff --git a/src/ast/statement_condition.cc b/src/ast/statement_condition.cc
new file mode 100644
index 0000000..dc2136f
--- /dev/null
+++ b/src/ast/statement_condition.cc
@@ -0,0 +1,37 @@
+// Copyright 2020 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/ast/statement_condition.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, StatementCondition condition) {
+ switch (condition) {
+ case StatementCondition::kNone:
+ out << "none";
+ break;
+ case StatementCondition::kIf:
+ out << "if";
+ break;
+ case StatementCondition::kUnless:
+ out << "unless";
+ break;
+ }
+
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/statement_condition.h b/src/ast/statement_condition.h
new file mode 100644
index 0000000..fa4149e
--- /dev/null
+++ b/src/ast/statement_condition.h
@@ -0,0 +1,31 @@
+// Copyright 2020 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_AST_STATEMENT_CONDITION_H_
+#define SRC_AST_STATEMENT_CONDITION_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// Type of condition attached to a statement
+enum class StatementCondition { kNone = 0, kIf, kUnless };
+
+std::ostream& operator<<(std::ostream& out, StatementCondition condition);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_STATEMENT_CONDITION_H_
diff --git a/src/ast/storage_class.cc b/src/ast/storage_class.cc
new file mode 100644
index 0000000..e0c71f7
--- /dev/null
+++ b/src/ast/storage_class.cc
@@ -0,0 +1,71 @@
+// Copyright 2020 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/ast/storage_class.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, StorageClass sc) {
+ switch (sc) {
+ case StorageClass::kNone: {
+ out << "none";
+ break;
+ }
+ case StorageClass::kInput: {
+ out << "input";
+ break;
+ }
+ case StorageClass::kOutput: {
+ out << "output";
+ break;
+ }
+ case StorageClass::kUniform: {
+ out << "uniform";
+ break;
+ }
+ case StorageClass::kWorkgroup: {
+ out << "workgroup";
+ break;
+ }
+ case StorageClass::kUniformConstant: {
+ out << "uniform_constant";
+ break;
+ }
+ case StorageClass::kStorageBuffer: {
+ out << "storage_buffer";
+ break;
+ }
+ case StorageClass::kImage: {
+ out << "image";
+ break;
+ }
+ case StorageClass::kPushConstant: {
+ out << "push_constant";
+ break;
+ }
+ case StorageClass::kPrivate: {
+ out << "private";
+ break;
+ }
+ case StorageClass::kFunction: {
+ out << "function";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/storage_class.h b/src/ast/storage_class.h
new file mode 100644
index 0000000..3a25fed
--- /dev/null
+++ b/src/ast/storage_class.h
@@ -0,0 +1,43 @@
+// Copyright 2020 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_AST_STORAGE_CLASS_H_
+#define SRC_AST_STORAGE_CLASS_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// Storage class of a given pointer.
+enum class StorageClass {
+ kNone = -1,
+ kInput,
+ kOutput,
+ kUniform,
+ kWorkgroup,
+ kUniformConstant,
+ kStorageBuffer,
+ kImage,
+ kPushConstant,
+ kPrivate,
+ kFunction
+};
+
+std::ostream& operator<<(std::ostream& out, StorageClass sc);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_STORAGE_CLASS_H_
diff --git a/src/ast/struct.cc b/src/ast/struct.cc
new file mode 100644
index 0000000..1cdf866
--- /dev/null
+++ b/src/ast/struct.cc
@@ -0,0 +1,49 @@
+// Copyright 2020 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/ast/struct.h"
+
+namespace tint {
+namespace ast {
+
+Struct::Struct(StructDecoration decoration,
+ std::vector<std::unique_ptr<StructMember>> members)
+ : Node(), decoration_(decoration), members_(std::move(members)) {}
+
+Struct::Struct(const Source& source,
+ StructDecoration decoration,
+ std::vector<std::unique_ptr<StructMember>> members)
+ : Node(source), decoration_(decoration), members_(std::move(members)) {}
+
+Struct::~Struct() = default;
+
+bool Struct::IsValid() const {
+ return true;
+}
+
+void Struct::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ if (decoration_ != StructDecoration::kNone) {
+ out << "[[" << decoration_ << "]] ";
+ }
+ out << "Struct{" << std::endl;
+ for (const auto& member : members_) {
+ member->to_str(out, indent + 2);
+ }
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct.h b/src/ast/struct.h
new file mode 100644
index 0000000..a3d2e12
--- /dev/null
+++ b/src/ast/struct.h
@@ -0,0 +1,87 @@
+// Copyright 2020 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_AST_STRUCT_H_
+#define SRC_AST_STRUCT_H_
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/ast/node.h"
+#include "src/ast/struct_decoration.h"
+#include "src/ast/struct_member.h"
+
+namespace tint {
+namespace ast {
+
+/// A struct statement.
+class Struct : public Node {
+ public:
+ /// Create a new empty struct statement
+ Struct() = default;
+ /// Create a new struct statement
+ /// @param decoration The struct decorations
+ /// @param members The struct members
+ Struct(StructDecoration decoration,
+ std::vector<std::unique_ptr<StructMember>> members);
+ /// Create a new struct statement
+ /// @param source The input source for the import statement
+ /// @param decoration The struct decorations
+ /// @param members The struct members
+ Struct(const Source& source,
+ StructDecoration decoration,
+ std::vector<std::unique_ptr<StructMember>> members);
+ /// Move constructor
+ Struct(Struct&&) = default;
+
+ ~Struct() override;
+
+ /// Sets the struct decoration
+ /// @param deco the decoration to set
+ void set_decoration(StructDecoration deco) { decoration_ = deco; }
+ /// @returns the struct decoration
+ StructDecoration decoration() const { return decoration_; }
+
+ /// Sets the struct members
+ /// @param members the members to set
+ void set_members(std::vector<std::unique_ptr<StructMember>> members) {
+ members_ = std::move(members);
+ }
+ /// @returns the members
+ const std::vector<std::unique_ptr<StructMember>>& members() const {
+ return members_;
+ }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ Struct(const Struct&) = delete;
+
+ StructDecoration decoration_ = StructDecoration::kNone;
+ std::vector<std::unique_ptr<StructMember>> members_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_STRUCT_H_
diff --git a/src/ast/struct_decoration.cc b/src/ast/struct_decoration.cc
new file mode 100644
index 0000000..10ee485
--- /dev/null
+++ b/src/ast/struct_decoration.cc
@@ -0,0 +1,35 @@
+// Copyright 2020 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/ast/struct_decoration.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, StructDecoration stage) {
+ switch (stage) {
+ case StructDecoration::kNone: {
+ out << "none";
+ break;
+ }
+ case StructDecoration::kBlock: {
+ out << "block";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct_decoration.h b/src/ast/struct_decoration.h
new file mode 100644
index 0000000..fab582c
--- /dev/null
+++ b/src/ast/struct_decoration.h
@@ -0,0 +1,31 @@
+// Copyright 2020 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_AST_STRUCT_DECORATION_H_
+#define SRC_AST_STRUCT_DECORATION_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// The struct decorations
+enum class StructDecoration { kNone = -1, kBlock };
+
+std::ostream& operator<<(std::ostream& out, StructDecoration stage);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_STRUCT_DECORATION_H_
diff --git a/src/ast/struct_member.cc b/src/ast/struct_member.cc
new file mode 100644
index 0000000..4ad5230
--- /dev/null
+++ b/src/ast/struct_member.cc
@@ -0,0 +1,62 @@
+// Copyright 2020 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/ast/struct_member.h"
+
+namespace tint {
+namespace ast {
+
+StructMember::StructMember(
+ const std::string& name,
+ type::Type* type,
+ std::vector<std::unique_ptr<StructMemberDecoration>> decorations)
+ : Node(), name_(name), type_(type), decorations_(std::move(decorations)) {}
+
+StructMember::StructMember(
+ const Source& source,
+ const std::string& name,
+ type::Type* type,
+ std::vector<std::unique_ptr<StructMemberDecoration>> decorations)
+ : Node(source),
+ name_(name),
+ type_(type),
+ decorations_(std::move(decorations)) {}
+
+StructMember::~StructMember() = default;
+
+bool StructMember::IsValid() const {
+ if (name_.length() == 0) {
+ return false;
+ }
+ if (type_ == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+void StructMember::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "StructMember{";
+ if (decorations_.size() > 0) {
+ out << "[[ ";
+ for (const auto& deco : decorations_)
+ out << deco->to_str() << " ";
+ out << "]] ";
+ }
+
+ out << name_ << ": " << type_->type_name() << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct_member.h b/src/ast/struct_member.h
new file mode 100644
index 0000000..0aceb81
--- /dev/null
+++ b/src/ast/struct_member.h
@@ -0,0 +1,100 @@
+// Copyright 2020 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_AST_STRUCT_MEMBER_H_
+#define SRC_AST_STRUCT_MEMBER_H_
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/ast/node.h"
+#include "src/ast/struct_member_decoration.h"
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+
+/// A struct member statement.
+class StructMember : public Node {
+ public:
+ /// Create a new empty struct member statement
+ StructMember() = default;
+ /// Create a new struct member statement
+ /// @param name The struct member name
+ /// @param type The struct member type
+ /// @param decorations The struct member decorations
+ StructMember(
+ const std::string& name,
+ type::Type* type,
+ std::vector<std::unique_ptr<StructMemberDecoration>> decorations);
+ /// Create a new struct member statement
+ /// @param source The input source for the struct member statement
+ /// @param name The struct member name
+ /// @param type The struct member type
+ /// @param decorations The struct member decorations
+ StructMember(
+ const Source& source,
+ const std::string& name,
+ type::Type* type,
+ std::vector<std::unique_ptr<StructMemberDecoration>> decorations);
+ /// Move constructor
+ StructMember(StructMember&&) = default;
+
+ ~StructMember() override;
+
+ /// Sets the name
+ /// @param name the name to set
+ void set_name(const std::string& name) { name_ = name; }
+ /// @returns the name
+ const std::string& name() const { return name_; }
+ /// Sets the type
+ /// @param type the type to set
+ void set_type(type::Type* type) { type_ = type; }
+ /// @returns the type
+ type::Type* type() const { return type_; }
+ /// Sets the decorations
+ /// @param decorations the decorations
+ void set_decorations(
+ std::vector<std::unique_ptr<StructMemberDecoration>> decorations) {
+ decorations_ = std::move(decorations);
+ }
+ /// @returns the decorations
+ const std::vector<std::unique_ptr<StructMemberDecoration>>& decorations()
+ const {
+ return decorations_;
+ }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ StructMember(const StructMember&) = delete;
+
+ std::string name_;
+ type::Type* type_ = nullptr;
+ std::vector<std::unique_ptr<StructMemberDecoration>> decorations_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_STRUCT_MEMBER_H_
diff --git a/src/ast/struct_member_decoration.cc b/src/ast/struct_member_decoration.cc
new file mode 100644
index 0000000..bcb091a
--- /dev/null
+++ b/src/ast/struct_member_decoration.cc
@@ -0,0 +1,34 @@
+// Copyright 2020 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/ast/struct_member_decoration.h"
+
+#include <assert.h>
+
+#include "src/ast/struct_member_offset_decoration.h"
+
+namespace tint {
+namespace ast {
+
+StructMemberDecoration::StructMemberDecoration() = default;
+
+StructMemberDecoration::~StructMemberDecoration() = default;
+
+StructMemberOffsetDecoration* StructMemberDecoration::AsOffset() {
+ assert(IsOffset());
+ return static_cast<StructMemberOffsetDecoration*>(this);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct_member_decoration.h b/src/ast/struct_member_decoration.h
new file mode 100644
index 0000000..525bb64
--- /dev/null
+++ b/src/ast/struct_member_decoration.h
@@ -0,0 +1,46 @@
+// Copyright 2020 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_AST_STRUCT_MEMBER_DECORATION_H_
+#define SRC_AST_STRUCT_MEMBER_DECORATION_H_
+
+#include <string>
+
+namespace tint {
+namespace ast {
+
+class StructMemberOffsetDecoration;
+
+/// A decoration attached to a struct member
+class StructMemberDecoration {
+ public:
+ virtual ~StructMemberDecoration();
+
+ /// @returns true if this is an offset decoration
+ virtual bool IsOffset() const { return false; }
+
+ /// @returns the decoration as an offset decoration
+ StructMemberOffsetDecoration* AsOffset();
+
+ /// @returns the decoration as a string
+ virtual std::string to_str() const = 0;
+
+ protected:
+ StructMemberDecoration();
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_STRUCT_MEMBER_DECORATION_H_
diff --git a/src/ast/struct_member_offset_decoration.cc b/src/ast/struct_member_offset_decoration.cc
new file mode 100644
index 0000000..0a70a95
--- /dev/null
+++ b/src/ast/struct_member_offset_decoration.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 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/ast/struct_member_offset_decoration.h"
+
+namespace tint {
+namespace ast {
+
+StructMemberOffsetDecoration::StructMemberOffsetDecoration(size_t offset)
+ : offset_(offset) {}
+
+StructMemberOffsetDecoration::~StructMemberOffsetDecoration() = default;
+
+std::string StructMemberOffsetDecoration::to_str() const {
+ return "offset " + std::to_string(offset_);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct_member_offset_decoration.h b/src/ast/struct_member_offset_decoration.h
new file mode 100644
index 0000000..8e15a63
--- /dev/null
+++ b/src/ast/struct_member_offset_decoration.h
@@ -0,0 +1,51 @@
+// Copyright 2020 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_AST_STRUCT_MEMBER_OFFSET_DECORATION_H_
+#define SRC_AST_STRUCT_MEMBER_OFFSET_DECORATION_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "src/ast/struct_member_decoration.h"
+
+namespace tint {
+namespace ast {
+
+/// A struct member offset decoration
+class StructMemberOffsetDecoration : public StructMemberDecoration {
+ public:
+ /// constructor
+ /// @param offset the offset value
+ explicit StructMemberOffsetDecoration(size_t offset);
+ ~StructMemberOffsetDecoration() override;
+
+ /// @returns true if this is an offset decoration
+ bool IsOffset() const override { return true; }
+
+ /// @returns the offset value
+ size_t offset() const { return offset_; }
+
+ /// @returns the decoration as a string
+ std::string to_str() const override;
+
+ private:
+ size_t offset_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_STRUCT_MEMBER_OFFSET_DECORATION_H_
diff --git a/src/ast/struct_member_offset_decoration_test.cc b/src/ast/struct_member_offset_decoration_test.cc
new file mode 100644
index 0000000..cd2e2b9
--- /dev/null
+++ b/src/ast/struct_member_offset_decoration_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2020 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/ast/struct_member_offset_decoration.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using StructMemberOffsetDecorationTest = testing::Test;
+
+TEST_F(StructMemberOffsetDecorationTest, Creation) {
+ StructMemberOffsetDecoration d{2};
+ EXPECT_EQ(2, d.offset());
+}
+
+TEST_F(StructMemberOffsetDecorationTest, Is) {
+ StructMemberOffsetDecoration d{2};
+ EXPECT_TRUE(d.IsOffset());
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct_member_test.cc b/src/ast/struct_member_test.cc
new file mode 100644
index 0000000..9451d99
--- /dev/null
+++ b/src/ast/struct_member_test.cc
@@ -0,0 +1,92 @@
+// Copyright 2020 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/ast/struct_member.h"
+
+#include <sstream>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "src/ast/struct_member_offset_decoration.h"
+#include "src/ast/type/i32_type.h"
+
+namespace tint {
+namespace ast {
+
+using StructMemberTest = testing::Test;
+
+TEST_F(StructMemberTest, Creation) {
+ type::I32Type i32;
+ std::vector<std::unique_ptr<StructMemberDecoration>> decorations;
+ decorations.emplace_back(std::make_unique<StructMemberOffsetDecoration>(4));
+
+ StructMember st{"a", &i32, std::move(decorations)};
+ EXPECT_EQ(st.name(), "a");
+ EXPECT_EQ(st.type(), &i32);
+ EXPECT_EQ(st.decorations().size(), 1);
+ EXPECT_TRUE(st.decorations()[0]->IsOffset());
+ EXPECT_EQ(st.line(), 0);
+ EXPECT_EQ(st.column(), 0);
+}
+
+TEST_F(StructMemberTest, CreationWithSource) {
+ type::I32Type i32;
+ Source s{27, 4};
+
+ StructMember st{s, "a", &i32, {}};
+ EXPECT_EQ(st.name(), "a");
+ EXPECT_EQ(st.type(), &i32);
+ EXPECT_EQ(st.decorations().size(), 0);
+ EXPECT_EQ(st.line(), 27);
+ EXPECT_EQ(st.column(), 4);
+}
+
+TEST_F(StructMemberTest, IsValid) {
+ type::I32Type i32;
+ StructMember st{"a", &i32, {}};
+ EXPECT_TRUE(st.IsValid());
+}
+
+TEST_F(StructMemberTest, IsValid_EmptyName) {
+ type::I32Type i32;
+ StructMember st{"", &i32, {}};
+ EXPECT_FALSE(st.IsValid());
+}
+
+TEST_F(StructMemberTest, IsValid_NullType) {
+ StructMember st{"a", nullptr, {}};
+ EXPECT_FALSE(st.IsValid());
+}
+
+TEST_F(StructMemberTest, ToStr) {
+ type::I32Type i32;
+ std::vector<std::unique_ptr<StructMemberDecoration>> decorations;
+ decorations.emplace_back(std::make_unique<StructMemberOffsetDecoration>(4));
+
+ StructMember st{"a", &i32, std::move(decorations)};
+ std::ostringstream out;
+ st.to_str(out, 0);
+ EXPECT_EQ(out.str(), "StructMember{[[ offset 4 ]] a: __i32}\n");
+}
+
+TEST_F(StructMemberTest, ToStrNoDecorations) {
+ type::I32Type i32;
+ StructMember st{"a", &i32, {}};
+ std::ostringstream out;
+ st.to_str(out, 0);
+ EXPECT_EQ(out.str(), "StructMember{a: __i32}\n");
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/struct_test.cc b/src/ast/struct_test.cc
new file mode 100644
index 0000000..9802270
--- /dev/null
+++ b/src/ast/struct_test.cc
@@ -0,0 +1,80 @@
+// Copyright 2020 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/ast/struct.h"
+
+#include <sstream>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "src/ast/struct_decoration.h"
+#include "src/ast/struct_member.h"
+#include "src/ast/type/i32_type.h"
+
+namespace tint {
+namespace ast {
+
+using StructTest = testing::Test;
+
+TEST_F(StructTest, Creation) {
+ type::I32Type i32;
+ std::vector<std::unique_ptr<StructMember>> members;
+ members.push_back(std::make_unique<StructMember>(
+ "a", &i32, std::vector<std::unique_ptr<StructMemberDecoration>>()));
+
+ Struct s{StructDecoration::kNone, std::move(members)};
+ EXPECT_EQ(s.members().size(), 1);
+ EXPECT_EQ(s.decoration(), StructDecoration::kNone);
+ EXPECT_EQ(s.line(), 0);
+ EXPECT_EQ(s.column(), 0);
+}
+
+TEST_F(StructTest, CreationWithSource) {
+ type::I32Type i32;
+ Source source{27, 4};
+ std::vector<std::unique_ptr<StructMember>> members;
+ members.emplace_back(std::make_unique<StructMember>(
+ "a", &i32, std::vector<std::unique_ptr<StructMemberDecoration>>()));
+
+ Struct s{source, StructDecoration::kNone, std::move(members)};
+ EXPECT_EQ(s.members().size(), 1);
+ EXPECT_EQ(s.decoration(), StructDecoration::kNone);
+ EXPECT_EQ(s.line(), 27);
+ EXPECT_EQ(s.column(), 4);
+}
+
+TEST_F(StructTest, IsValid) {
+ Struct s;
+ EXPECT_TRUE(s.IsValid());
+}
+
+TEST_F(StructTest, ToStr) {
+ type::I32Type i32;
+ Source source{27, 4};
+ std::vector<std::unique_ptr<StructMember>> members;
+ members.emplace_back(std::make_unique<StructMember>(
+ "a", &i32, std::vector<std::unique_ptr<StructMemberDecoration>>()));
+
+ Struct s{source, StructDecoration::kNone, std::move(members)};
+
+ std::ostringstream out;
+ s.to_str(out, 0);
+ EXPECT_EQ(out.str(), R"(Struct{
+ StructMember{a: __i32}
+}
+)");
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/switch_statement.cc b/src/ast/switch_statement.cc
new file mode 100644
index 0000000..aabc242
--- /dev/null
+++ b/src/ast/switch_statement.cc
@@ -0,0 +1,58 @@
+// Copyright 2020 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/ast/switch_statement.h"
+
+#include "src/ast/case_statement.h"
+
+namespace tint {
+namespace ast {
+
+SwitchStatement::SwitchStatement() : Statement() {}
+
+SwitchStatement::SwitchStatement(
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<CaseStatement>> body)
+ : Statement(), condition_(std::move(condition)), body_(std::move(body)) {}
+
+SwitchStatement::SwitchStatement(
+ const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<CaseStatement>> body)
+ : Statement(source),
+ condition_(std::move(condition)),
+ body_(std::move(body)) {}
+
+SwitchStatement::~SwitchStatement() = default;
+
+bool SwitchStatement::IsValid() const {
+ return condition_ != nullptr;
+}
+
+void SwitchStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Switch{" << std::endl;
+ condition_->to_str(out, indent + 2);
+ make_indent(out, indent + 2);
+ out << "{" << std::endl;
+
+ for (const auto& stmt : body_)
+ stmt->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/switch_statement.h b/src/ast/switch_statement.h
new file mode 100644
index 0000000..9d13995
--- /dev/null
+++ b/src/ast/switch_statement.h
@@ -0,0 +1,92 @@
+// Copyright 2020 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_AST_SWITCH_STATEMENT_H_
+#define SRC_AST_SWITCH_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/statement.h"
+#include "src/ast/statement_condition.h"
+
+namespace tint {
+namespace ast {
+
+/// A switch statement
+class SwitchStatement : public Statement {
+ public:
+ /// Constructor
+ SwitchStatement();
+ /// Constructor
+ /// @param condition the switch condition
+ /// @param body the switch body
+ SwitchStatement(std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<CaseStatement>> body);
+ /// Constructor
+ /// @param source the source information
+ /// @param condition the switch condition
+ /// @param body the switch body
+ SwitchStatement(const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<CaseStatement>> body);
+ /// Move constructor
+ SwitchStatement(SwitchStatement&&) = default;
+ ~SwitchStatement() override;
+
+ /// Sets the condition for the switch statement
+ /// @param condition the condition to set
+ void set_condition(std::unique_ptr<Expression> condition) {
+ condition_ = std::move(condition);
+ }
+ /// @returns the switch condition or nullptr if none set
+ Expression* condition() const { return condition_.get(); }
+ /// @returns true if this is a default statement
+ bool IsDefault() const { return condition_ == nullptr; }
+
+ /// Sets the switch body
+ /// @param body the switch body
+ void set_body(std::vector<std::unique_ptr<CaseStatement>> body) {
+ body_ = std::move(body);
+ }
+ /// @returns the Switch body
+ const std::vector<std::unique_ptr<CaseStatement>>& body() const {
+ return body_;
+ }
+
+ /// @returns true if this is a switch statement
+ bool IsSwitch() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ SwitchStatement(const SwitchStatement&) = delete;
+
+ std::unique_ptr<Expression> condition_;
+ std::vector<std::unique_ptr<CaseStatement>> body_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_SWITCH_STATEMENT_H_
diff --git a/src/ast/type/alias_type.cc b/src/ast/type/alias_type.cc
new file mode 100644
index 0000000..199557e
--- /dev/null
+++ b/src/ast/type/alias_type.cc
@@ -0,0 +1,32 @@
+// Copyright 2020 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/ast/type/alias_type.h"
+
+#include <assert.h>
+
+namespace tint {
+namespace ast {
+namespace type {
+
+AliasType::AliasType(const std::string& name, Type* subtype)
+ : name_(name), subtype_(subtype) {
+ assert(subtype_);
+}
+
+AliasType::~AliasType() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/alias_type.h b/src/ast/type/alias_type.h
new file mode 100644
index 0000000..a555951
--- /dev/null
+++ b/src/ast/type/alias_type.h
@@ -0,0 +1,59 @@
+// Copyright 2020 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_AST_TYPE_ALIAS_TYPE_H_
+#define SRC_AST_TYPE_ALIAS_TYPE_H_
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A type alias type. Holds a name a pointer to another type.
+class AliasType : public Type {
+ public:
+ /// Constructor
+ /// @param name the alias name
+ /// @param subtype the alias'd type
+ AliasType(const std::string& name, Type* subtype);
+ /// Move constructor
+ AliasType(AliasType&&) = default;
+ ~AliasType() override;
+
+ /// @returns true if the type is an alias type
+ bool IsAlias() const override { return true; }
+
+ /// @returns the alias name
+ const std::string& name() const { return name_; }
+ /// @returns the alias type
+ Type* type() const { return subtype_; }
+
+ /// @returns the name for this type
+ std::string type_name() const override {
+ return "__alias_" + name_ + subtype_->type_name();
+ }
+
+ private:
+ std::string name_;
+ Type* subtype_ = nullptr;
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_ALIAS_TYPE_H_
diff --git a/src/ast/type/alias_type_test.cc b/src/ast/type/alias_type_test.cc
new file mode 100644
index 0000000..e3c3126
--- /dev/null
+++ b/src/ast/type/alias_type_test.cc
@@ -0,0 +1,58 @@
+// Copyright 2020 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/ast/type/alias_type.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/u32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using AliasTypeTest = testing::Test;
+
+TEST_F(AliasTypeTest, Create) {
+ U32Type u32;
+ AliasType a{"a_type", &u32};
+ EXPECT_EQ(a.name(), "a_type");
+ EXPECT_EQ(a.type(), &u32);
+}
+
+TEST_F(AliasTypeTest, Is) {
+ I32Type i32;
+
+ AliasType at{"a", &i32};
+ EXPECT_TRUE(at.IsAlias());
+ EXPECT_FALSE(at.IsArray());
+ EXPECT_FALSE(at.IsBool());
+ EXPECT_FALSE(at.IsF32());
+ EXPECT_FALSE(at.IsI32());
+ EXPECT_FALSE(at.IsMatrix());
+ EXPECT_FALSE(at.IsPointer());
+ EXPECT_FALSE(at.IsStruct());
+ EXPECT_FALSE(at.IsU32());
+ EXPECT_FALSE(at.IsVector());
+}
+
+TEST_F(AliasTypeTest, TypeName) {
+ I32Type i32;
+ AliasType at{"Particle", &i32};
+ EXPECT_EQ(at.type_name(), "__alias_Particle__i32");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/array_type.cc b/src/ast/type/array_type.cc
new file mode 100644
index 0000000..c9eb6b6
--- /dev/null
+++ b/src/ast/type/array_type.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 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/ast/type/array_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+ArrayType::ArrayType(Type* subtype) : subtype_(subtype) {}
+
+ArrayType::ArrayType(Type* subtype, size_t size)
+ : subtype_(subtype), size_(size) {}
+
+ArrayType::~ArrayType() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/array_type.h b/src/ast/type/array_type.h
new file mode 100644
index 0000000..28d25e0
--- /dev/null
+++ b/src/ast/type/array_type.h
@@ -0,0 +1,73 @@
+// Copyright 2020 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_AST_TYPE_ARRAY_TYPE_H_
+#define SRC_AST_TYPE_ARRAY_TYPE_H_
+
+#include <assert.h>
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// An array type. If size is zero then it is a runtime array.
+class ArrayType : public Type {
+ public:
+ /// Constructor for runtime array
+ /// @param subtype the type of the array elements
+ explicit ArrayType(Type* subtype);
+ /// Constructor
+ /// @param subtype the type of the array elements
+ /// @param size the number of elements in the array
+ ArrayType(Type* subtype, size_t size);
+ /// Move constructor
+ ArrayType(ArrayType&&) = default;
+ ~ArrayType() override;
+
+ /// @returns true if the type is an array type
+ bool IsArray() const override { return true; }
+ /// @returns true if this is a runtime array.
+ /// i.e. the size is determined at runtime
+ bool IsRuntimeArray() const { return size_ == 0; }
+
+ /// @returns the array type
+ Type* type() const { return subtype_; }
+ /// @returns the array size. Size is 0 for a runtime array
+ size_t size() const { return size_; }
+
+ /// @returns the name for th type
+ std::string type_name() const override {
+ assert(subtype_);
+
+ std::string type_name = "__array" + subtype_->type_name();
+ if (!IsRuntimeArray())
+ type_name += "_" + std::to_string(size_);
+
+ return type_name;
+ }
+
+ private:
+ Type* subtype_ = nullptr;
+ size_t size_ = 0;
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_ARRAY_TYPE_H_
diff --git a/src/ast/type/array_type_test.cc b/src/ast/type/array_type_test.cc
new file mode 100644
index 0000000..5495f5b
--- /dev/null
+++ b/src/ast/type/array_type_test.cc
@@ -0,0 +1,69 @@
+// Copyright 2020 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/ast/type/array_type.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/u32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using ArrayTypeTest = testing::Test;
+
+TEST_F(ArrayTypeTest, CreateSizedArray) {
+ U32Type u32;
+ ArrayType arr{&u32, 3};
+ EXPECT_EQ(arr.type(), &u32);
+ EXPECT_EQ(arr.size(), 3);
+ EXPECT_TRUE(arr.IsArray());
+ EXPECT_FALSE(arr.IsRuntimeArray());
+}
+
+TEST_F(ArrayTypeTest, CreateRuntimeArray) {
+ U32Type u32;
+ ArrayType arr{&u32};
+ EXPECT_EQ(arr.type(), &u32);
+ EXPECT_EQ(arr.size(), 0);
+ EXPECT_TRUE(arr.IsArray());
+ EXPECT_TRUE(arr.IsRuntimeArray());
+}
+
+TEST_F(ArrayTypeTest, Is) {
+ I32Type i32;
+
+ ArrayType arr{&i32, 3};
+ EXPECT_FALSE(arr.IsAlias());
+ EXPECT_TRUE(arr.IsArray());
+ EXPECT_FALSE(arr.IsBool());
+ EXPECT_FALSE(arr.IsF32());
+ EXPECT_FALSE(arr.IsI32());
+ EXPECT_FALSE(arr.IsMatrix());
+ EXPECT_FALSE(arr.IsPointer());
+ EXPECT_FALSE(arr.IsStruct());
+ EXPECT_FALSE(arr.IsU32());
+ EXPECT_FALSE(arr.IsVector());
+}
+
+TEST_F(ArrayTypeTest, TypeName) {
+ I32Type i32;
+ ArrayType arr{&i32, 3};
+ EXPECT_EQ(arr.type_name(), "__array__i32_3");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/bool_type.cc b/src/ast/type/bool_type.cc
new file mode 100644
index 0000000..d584181
--- /dev/null
+++ b/src/ast/type/bool_type.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/ast/type/bool_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+BoolType::BoolType() = default;
+
+BoolType::~BoolType() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/bool_type.h b/src/ast/type/bool_type.h
new file mode 100644
index 0000000..79c4d63
--- /dev/null
+++ b/src/ast/type/bool_type.h
@@ -0,0 +1,46 @@
+// Copyright 2020 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_AST_TYPE_BOOL_TYPE_H_
+#define SRC_AST_TYPE_BOOL_TYPE_H_
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A boolean type
+class BoolType : public Type {
+ public:
+ /// Constructor
+ BoolType();
+ /// Move constructor
+ BoolType(BoolType&&) = default;
+ ~BoolType() override;
+
+ /// @returns true if the type is a bool type
+ bool IsBool() const override { return true; }
+
+ /// @returns the name for this type
+ std::string type_name() const override { return "__bool"; }
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_BOOL_TYPE_H_
diff --git a/src/ast/type/bool_type_test.cc b/src/ast/type/bool_type_test.cc
new file mode 100644
index 0000000..6aacfe8
--- /dev/null
+++ b/src/ast/type/bool_type_test.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 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/ast/type/bool_type.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using BoolTypeTest = testing::Test;
+
+TEST_F(BoolTypeTest, Is) {
+ BoolType b;
+ EXPECT_FALSE(b.IsAlias());
+ EXPECT_FALSE(b.IsArray());
+ EXPECT_TRUE(b.IsBool());
+ EXPECT_FALSE(b.IsF32());
+ EXPECT_FALSE(b.IsI32());
+ EXPECT_FALSE(b.IsMatrix());
+ EXPECT_FALSE(b.IsPointer());
+ EXPECT_FALSE(b.IsStruct());
+ EXPECT_FALSE(b.IsU32());
+ EXPECT_FALSE(b.IsVector());
+}
+
+TEST_F(BoolTypeTest, TypeName) {
+ BoolType b;
+ EXPECT_EQ(b.type_name(), "__bool");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/f32_type.cc b/src/ast/type/f32_type.cc
new file mode 100644
index 0000000..065c22e
--- /dev/null
+++ b/src/ast/type/f32_type.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/ast/type/f32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+F32Type::F32Type() = default;
+
+F32Type::~F32Type() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/f32_type.h b/src/ast/type/f32_type.h
new file mode 100644
index 0000000..881ec4a
--- /dev/null
+++ b/src/ast/type/f32_type.h
@@ -0,0 +1,46 @@
+// Copyright 2020 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_AST_TYPE_F32_TYPE_H_
+#define SRC_AST_TYPE_F32_TYPE_H_
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A float 32 type
+class F32Type : public Type {
+ public:
+ /// Constructor
+ F32Type();
+ /// Move constructor
+ F32Type(F32Type&&) = default;
+ ~F32Type() override;
+
+ /// @returns true if the type is an f32 type
+ bool IsF32() const override { return true; }
+
+ /// @returns the name for this type
+ std::string type_name() const override { return "__f32"; }
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_F32_TYPE_H_
diff --git a/src/ast/type/f32_type_test.cc b/src/ast/type/f32_type_test.cc
new file mode 100644
index 0000000..a5287f0
--- /dev/null
+++ b/src/ast/type/f32_type_test.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 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/ast/type/f32_type.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using F32TypeTest = testing::Test;
+
+TEST_F(F32TypeTest, Is) {
+ F32Type f;
+ EXPECT_FALSE(f.IsAlias());
+ EXPECT_FALSE(f.IsArray());
+ EXPECT_FALSE(f.IsBool());
+ EXPECT_TRUE(f.IsF32());
+ EXPECT_FALSE(f.IsI32());
+ EXPECT_FALSE(f.IsMatrix());
+ EXPECT_FALSE(f.IsPointer());
+ EXPECT_FALSE(f.IsStruct());
+ EXPECT_FALSE(f.IsU32());
+ EXPECT_FALSE(f.IsVector());
+}
+
+TEST_F(F32TypeTest, TypeName) {
+ F32Type f;
+ EXPECT_EQ(f.type_name(), "__f32");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/i32_type.cc b/src/ast/type/i32_type.cc
new file mode 100644
index 0000000..0a7194d
--- /dev/null
+++ b/src/ast/type/i32_type.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/ast/type/i32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+I32Type::I32Type() = default;
+
+I32Type::~I32Type() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/i32_type.h b/src/ast/type/i32_type.h
new file mode 100644
index 0000000..ea60c13
--- /dev/null
+++ b/src/ast/type/i32_type.h
@@ -0,0 +1,46 @@
+// Copyright 2020 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_AST_TYPE_I32_TYPE_H_
+#define SRC_AST_TYPE_I32_TYPE_H_
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A signed int 32 type.
+class I32Type : public Type {
+ public:
+ /// Constructor
+ I32Type();
+ /// Move constructor
+ I32Type(I32Type&&) = default;
+ ~I32Type() override;
+
+ /// @returns true if the type is an i32 type
+ bool IsI32() const override { return true; }
+
+ /// @returns the name for this type
+ std::string type_name() const override { return "__i32"; }
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_I32_TYPE_H_
diff --git a/src/ast/type/i32_type_test.cc b/src/ast/type/i32_type_test.cc
new file mode 100644
index 0000000..67f2483
--- /dev/null
+++ b/src/ast/type/i32_type_test.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 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/ast/type/i32_type.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using I32TypeTest = testing::Test;
+
+TEST_F(I32TypeTest, Is) {
+ I32Type i;
+ EXPECT_FALSE(i.IsAlias());
+ EXPECT_FALSE(i.IsArray());
+ EXPECT_FALSE(i.IsBool());
+ EXPECT_FALSE(i.IsF32());
+ EXPECT_TRUE(i.IsI32());
+ EXPECT_FALSE(i.IsMatrix());
+ EXPECT_FALSE(i.IsPointer());
+ EXPECT_FALSE(i.IsStruct());
+ EXPECT_FALSE(i.IsU32());
+ EXPECT_FALSE(i.IsVector());
+}
+
+TEST_F(I32TypeTest, TypeName) {
+ I32Type i;
+ EXPECT_EQ(i.type_name(), "__i32");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/matrix_type.cc b/src/ast/type/matrix_type.cc
new file mode 100644
index 0000000..64aef3e
--- /dev/null
+++ b/src/ast/type/matrix_type.cc
@@ -0,0 +1,35 @@
+// Copyright 2020 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/ast/type/matrix_type.h"
+
+#include <assert.h>
+
+namespace tint {
+namespace ast {
+namespace type {
+
+MatrixType::MatrixType(Type* subtype, size_t rows, size_t columns)
+ : subtype_(subtype), rows_(rows), columns_(columns) {
+ assert(rows > 1);
+ assert(rows < 5);
+ assert(columns > 1);
+ assert(columns < 5);
+}
+
+MatrixType::~MatrixType() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/matrix_type.h b/src/ast/type/matrix_type.h
new file mode 100644
index 0000000..f37d12a
--- /dev/null
+++ b/src/ast/type/matrix_type.h
@@ -0,0 +1,64 @@
+// Copyright 2020 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_AST_TYPE_MATRIX_TYPE_H_
+#define SRC_AST_TYPE_MATRIX_TYPE_H_
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A matrix type
+class MatrixType : public Type {
+ public:
+ /// Constructor
+ /// @param subtype type matrix type
+ /// @param rows the number of rows in the matrix
+ /// @param columns the number of columns in the matrix
+ MatrixType(Type* subtype, size_t rows, size_t columns);
+ /// Move constructor
+ MatrixType(MatrixType&&) = default;
+ ~MatrixType() override;
+
+ /// @returns true if the type is a matrix type
+ bool IsMatrix() const override { return true; }
+
+ /// @returns the type of the matrix
+ Type* type() const { return subtype_; }
+ /// @returns the number of rows in the matrix
+ size_t rows() const { return rows_; }
+ /// @returns the number of columns in the matrix
+ size_t columns() const { return columns_; }
+
+ /// @returns the name for this type
+ std::string type_name() const override {
+ return "__mat_" + std::to_string(rows_) + "_" + std::to_string(columns_) +
+ subtype_->type_name();
+ }
+
+ private:
+ Type* subtype_ = nullptr;
+ size_t rows_ = 2;
+ size_t columns_ = 2;
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_MATRIX_TYPE_H_
diff --git a/src/ast/type/matrix_type_test.cc b/src/ast/type/matrix_type_test.cc
new file mode 100644
index 0000000..0bdb7ea
--- /dev/null
+++ b/src/ast/type/matrix_type_test.cc
@@ -0,0 +1,57 @@
+// Copyright 2020 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/ast/type/matrix_type.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using MatrixTypeTest = testing::Test;
+
+TEST_F(MatrixTypeTest, Creation) {
+ I32Type i32;
+ MatrixType m{&i32, 2, 4};
+ EXPECT_EQ(m.type(), &i32);
+ EXPECT_EQ(m.rows(), 2);
+ EXPECT_EQ(m.columns(), 4);
+}
+
+TEST_F(MatrixTypeTest, Is) {
+ I32Type i32;
+ MatrixType m{&i32, 2, 3};
+ EXPECT_FALSE(m.IsAlias());
+ EXPECT_FALSE(m.IsArray());
+ EXPECT_FALSE(m.IsBool());
+ EXPECT_FALSE(m.IsF32());
+ EXPECT_FALSE(m.IsI32());
+ EXPECT_TRUE(m.IsMatrix());
+ EXPECT_FALSE(m.IsPointer());
+ EXPECT_FALSE(m.IsStruct());
+ EXPECT_FALSE(m.IsU32());
+ EXPECT_FALSE(m.IsVector());
+}
+
+TEST_F(MatrixTypeTest, TypeName) {
+ I32Type i32;
+ MatrixType m{&i32, 2, 3};
+ EXPECT_EQ(m.type_name(), "__mat_2_3__i32");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/pointer_type.cc b/src/ast/type/pointer_type.cc
new file mode 100644
index 0000000..2b8e9ce
--- /dev/null
+++ b/src/ast/type/pointer_type.cc
@@ -0,0 +1,28 @@
+// Copyright 2020 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/ast/type/pointer_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+PointerType::PointerType(Type* subtype, StorageClass storage_class)
+ : subtype_(subtype), storage_class_(storage_class) {}
+
+PointerType::~PointerType() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/pointer_type.h b/src/ast/type/pointer_type.h
new file mode 100644
index 0000000..40ea116
--- /dev/null
+++ b/src/ast/type/pointer_type.h
@@ -0,0 +1,63 @@
+// Copyright 2020 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_AST_TYPE_POINTER_TYPE_H_
+#define SRC_AST_TYPE_POINTER_TYPE_H_
+
+#include <sstream>
+#include <string>
+
+#include "src/ast/storage_class.h"
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A pointer type.
+class PointerType : public Type {
+ public:
+ /// Construtor
+ /// @param subtype the pointee type
+ /// @param storage_class the storage class of the pointer
+ explicit PointerType(Type* subtype, StorageClass storage_class);
+ /// Move constructor
+ PointerType(PointerType&&) = default;
+ ~PointerType() override;
+
+ /// @returns true if the type is a pointer type
+ bool IsPointer() const override { return true; }
+
+ /// @returns the pointee type
+ Type* type() const { return subtype_; }
+ /// @returns the storage class of the pointer
+ StorageClass storage_class() const { return storage_class_; }
+
+ /// @returns the name for this type
+ std::string type_name() const override {
+ std::ostringstream out;
+ out << "__ptr_" << storage_class_ << subtype_->type_name();
+ return out.str();
+ }
+
+ private:
+ Type* subtype_;
+ StorageClass storage_class_;
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_POINTER_TYPE_H_
diff --git a/src/ast/type/pointer_type_test.cc b/src/ast/type/pointer_type_test.cc
new file mode 100644
index 0000000..52d167f
--- /dev/null
+++ b/src/ast/type/pointer_type_test.cc
@@ -0,0 +1,56 @@
+// Copyright 2020 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/ast/type/pointer_type.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using PointerTypeTest = testing::Test;
+
+TEST_F(PointerTypeTest, Creation) {
+ I32Type i32;
+ PointerType p{&i32, StorageClass::kStorageBuffer};
+ EXPECT_EQ(p.type(), &i32);
+ EXPECT_EQ(p.storage_class(), StorageClass::kStorageBuffer);
+}
+
+TEST_F(PointerTypeTest, Is) {
+ I32Type i32;
+ PointerType p{&i32, StorageClass::kFunction};
+ EXPECT_FALSE(p.IsAlias());
+ EXPECT_FALSE(p.IsArray());
+ EXPECT_FALSE(p.IsBool());
+ EXPECT_FALSE(p.IsF32());
+ EXPECT_FALSE(p.IsI32());
+ EXPECT_FALSE(p.IsMatrix());
+ EXPECT_TRUE(p.IsPointer());
+ EXPECT_FALSE(p.IsStruct());
+ EXPECT_FALSE(p.IsU32());
+ EXPECT_FALSE(p.IsVector());
+}
+
+TEST_F(PointerTypeTest, TypeName) {
+ I32Type i32;
+ PointerType p{&i32, StorageClass::kWorkgroup};
+ EXPECT_EQ(p.type_name(), "__ptr_workgroup__i32");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/struct_type.cc b/src/ast/type/struct_type.cc
new file mode 100644
index 0000000..d2fcfdc
--- /dev/null
+++ b/src/ast/type/struct_type.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 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/ast/type/struct_type.h"
+
+#include <utility>
+
+namespace tint {
+namespace ast {
+namespace type {
+
+StructType::StructType(std::unique_ptr<Struct> impl)
+ : struct_(std::move(impl)) {}
+
+StructType::~StructType() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/struct_type.h b/src/ast/type/struct_type.h
new file mode 100644
index 0000000..c33cd27
--- /dev/null
+++ b/src/ast/type/struct_type.h
@@ -0,0 +1,62 @@
+// Copyright 2020 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_AST_TYPE_STRUCT_TYPE_H_
+#define SRC_AST_TYPE_STRUCT_TYPE_H_
+
+#include <memory>
+#include <string>
+
+#include "src/ast/struct.h"
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A structure type
+class StructType : public Type {
+ public:
+ /// Constructor
+ /// @param impl the struct data
+ explicit StructType(std::unique_ptr<Struct> impl);
+ /// Move constructor
+ StructType(StructType&&) = default;
+ ~StructType() override;
+
+ /// Sets the name of the struct
+ /// @param name the name to set
+ void set_name(const std::string& name) { name_ = name; }
+ /// @returns the struct name
+ const std::string& name() const { return name_; }
+
+ /// @returns true if the type is a struct type
+ bool IsStruct() const override { return true; }
+
+ /// @returns the struct name
+ Struct* impl() const { return struct_.get(); }
+
+ /// @returns the name for th type
+ std::string type_name() const override { return "__struct_" + name_; }
+
+ private:
+ std::string name_;
+ std::unique_ptr<Struct> struct_;
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_STRUCT_TYPE_H_
diff --git a/src/ast/type/struct_type_test.cc b/src/ast/type/struct_type_test.cc
new file mode 100644
index 0000000..1721079
--- /dev/null
+++ b/src/ast/type/struct_type_test.cc
@@ -0,0 +1,59 @@
+// Copyright 2020 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/ast/type/struct_type.h"
+
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using StructTypeTest = testing::Test;
+
+TEST_F(StructTypeTest, Creation) {
+ auto impl = std::make_unique<Struct>();
+ auto ptr = impl.get();
+ StructType s{std::move(impl)};
+ EXPECT_EQ(s.impl(), ptr);
+}
+
+TEST_F(StructTypeTest, Is) {
+ auto impl = std::make_unique<Struct>();
+ StructType s{std::move(impl)};
+ EXPECT_FALSE(s.IsAlias());
+ EXPECT_FALSE(s.IsArray());
+ EXPECT_FALSE(s.IsBool());
+ EXPECT_FALSE(s.IsF32());
+ EXPECT_FALSE(s.IsI32());
+ EXPECT_FALSE(s.IsMatrix());
+ EXPECT_FALSE(s.IsPointer());
+ EXPECT_TRUE(s.IsStruct());
+ EXPECT_FALSE(s.IsU32());
+ EXPECT_FALSE(s.IsVector());
+}
+
+TEST_F(StructTypeTest, TypeName) {
+ auto impl = std::make_unique<Struct>();
+ StructType s{std::move(impl)};
+ s.set_name("my_struct");
+ EXPECT_EQ(s.type_name(), "__struct_my_struct");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/type.cc b/src/ast/type/type.cc
new file mode 100644
index 0000000..00fe94d
--- /dev/null
+++ b/src/ast/type/type.cc
@@ -0,0 +1,96 @@
+// Copyright 2020 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/ast/type/type.h"
+
+#include <assert.h>
+
+#include "src/ast/type/alias_type.h"
+#include "src/ast/type/array_type.h"
+#include "src/ast/type/bool_type.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/matrix_type.h"
+#include "src/ast/type/pointer_type.h"
+#include "src/ast/type/struct_type.h"
+#include "src/ast/type/u32_type.h"
+#include "src/ast/type/vector_type.h"
+#include "src/ast/type/void_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+Type::Type() = default;
+
+Type::~Type() = default;
+
+AliasType* Type::AsAlias() {
+ assert(IsAlias());
+ return static_cast<AliasType*>(this);
+}
+
+ArrayType* Type::AsArray() {
+ assert(IsArray());
+ return static_cast<ArrayType*>(this);
+}
+
+BoolType* Type::AsBool() {
+ assert(IsBool());
+ return static_cast<BoolType*>(this);
+}
+
+F32Type* Type::AsF32() {
+ assert(IsF32());
+ return static_cast<F32Type*>(this);
+}
+
+I32Type* Type::AsI32() {
+ assert(IsI32());
+ return static_cast<I32Type*>(this);
+}
+
+MatrixType* Type::AsMatrix() {
+ assert(IsMatrix());
+ return static_cast<MatrixType*>(this);
+}
+
+PointerType* Type::AsPointer() {
+ assert(IsPointer());
+ return static_cast<PointerType*>(this);
+}
+
+StructType* Type::AsStruct() {
+ assert(IsStruct());
+ return static_cast<StructType*>(this);
+}
+
+U32Type* Type::AsU32() {
+ assert(IsU32());
+ return static_cast<U32Type*>(this);
+}
+
+VectorType* Type::AsVector() {
+ assert(IsVector());
+ return static_cast<VectorType*>(this);
+}
+
+VoidType* Type::AsVoid() {
+ assert(IsVoid());
+ return static_cast<VoidType*>(this);
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/type.h b/src/ast/type/type.h
new file mode 100644
index 0000000..cf66f3b
--- /dev/null
+++ b/src/ast/type/type.h
@@ -0,0 +1,100 @@
+// Copyright 2020 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_AST_TYPE_TYPE_H_
+#define SRC_AST_TYPE_TYPE_H_
+
+#include <string>
+
+namespace tint {
+namespace ast {
+namespace type {
+
+class AliasType;
+class ArrayType;
+class BoolType;
+class F32Type;
+class I32Type;
+class MatrixType;
+class PointerType;
+class StructType;
+class U32Type;
+class VectorType;
+class VoidType;
+
+/// Base class for a type in the system
+class Type {
+ public:
+ /// Move constructor
+ Type(Type&&) = default;
+ virtual ~Type();
+
+ /// @returns true if the type is an alias type
+ virtual bool IsAlias() const { return false; }
+ /// @returns true if the type is an array type
+ virtual bool IsArray() const { return false; }
+ /// @returns true if the type is a bool type
+ virtual bool IsBool() const { return false; }
+ /// @returns true if the type is an f32 type
+ virtual bool IsF32() const { return false; }
+ /// @returns true if the type is an i32 type
+ virtual bool IsI32() const { return false; }
+ /// @returns true if the type is a matrix type
+ virtual bool IsMatrix() const { return false; }
+ /// @returns true if the type is a ptr type
+ virtual bool IsPointer() const { return false; }
+ /// @returns true if the type is a struct type
+ virtual bool IsStruct() const { return false; }
+ /// @returns true if the type is a u32 type
+ virtual bool IsU32() const { return false; }
+ /// @returns true if the type is a vec type
+ virtual bool IsVector() const { return false; }
+ /// @returns true if the type is a void type
+ virtual bool IsVoid() const { return false; }
+
+ /// @returns the name for this type
+ virtual std::string type_name() const = 0;
+
+ /// @returns the type as an alias type
+ AliasType* AsAlias();
+ /// @returns the type as an array type
+ ArrayType* AsArray();
+ /// @returns the type as a bool type
+ BoolType* AsBool();
+ /// @returns the type as a f32 type
+ F32Type* AsF32();
+ /// @returns the type as an i32 type
+ I32Type* AsI32();
+ /// @returns the type as a matrix type
+ MatrixType* AsMatrix();
+ /// @returns the type as a pointer type
+ PointerType* AsPointer();
+ /// @returns the type as a struct type
+ StructType* AsStruct();
+ /// @returns the type as a u32 type
+ U32Type* AsU32();
+ /// @returns the type as a vector type
+ VectorType* AsVector();
+ /// @returns the type as a void type
+ VoidType* AsVoid();
+
+ protected:
+ Type();
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_TYPE_H_
diff --git a/src/ast/type/u32_type.cc b/src/ast/type/u32_type.cc
new file mode 100644
index 0000000..b155b1d
--- /dev/null
+++ b/src/ast/type/u32_type.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/ast/type/u32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+U32Type::U32Type() = default;
+
+U32Type::~U32Type() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/u32_type.h b/src/ast/type/u32_type.h
new file mode 100644
index 0000000..b0d9440
--- /dev/null
+++ b/src/ast/type/u32_type.h
@@ -0,0 +1,46 @@
+// Copyright 2020 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_AST_TYPE_U32_TYPE_H_
+#define SRC_AST_TYPE_U32_TYPE_H_
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A unsigned int 32 type.
+class U32Type : public Type {
+ public:
+ /// Constructor
+ U32Type();
+ /// Move constructor
+ U32Type(U32Type&&) = default;
+ ~U32Type() override;
+
+ /// @returns true if the type is a u32 type
+ bool IsU32() const override { return true; }
+
+ /// @returns the name for th type
+ std::string type_name() const override { return "__u32"; }
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_U32_TYPE_H_
diff --git a/src/ast/type/u32_type_test.cc b/src/ast/type/u32_type_test.cc
new file mode 100644
index 0000000..5f4508e
--- /dev/null
+++ b/src/ast/type/u32_type_test.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 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/ast/type/u32_type.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using U32TypeTest = testing::Test;
+
+TEST_F(U32TypeTest, Is) {
+ U32Type u;
+ EXPECT_FALSE(u.IsAlias());
+ EXPECT_FALSE(u.IsArray());
+ EXPECT_FALSE(u.IsBool());
+ EXPECT_FALSE(u.IsF32());
+ EXPECT_FALSE(u.IsI32());
+ EXPECT_FALSE(u.IsMatrix());
+ EXPECT_FALSE(u.IsPointer());
+ EXPECT_FALSE(u.IsStruct());
+ EXPECT_TRUE(u.IsU32());
+ EXPECT_FALSE(u.IsVector());
+}
+
+TEST_F(U32TypeTest, TypeName) {
+ U32Type u;
+ EXPECT_EQ(u.type_name(), "__u32");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/vector_type.cc b/src/ast/type/vector_type.cc
new file mode 100644
index 0000000..8530214
--- /dev/null
+++ b/src/ast/type/vector_type.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 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/ast/type/vector_type.h"
+
+#include <assert.h>
+
+namespace tint {
+namespace ast {
+namespace type {
+
+VectorType::VectorType(Type* subtype, size_t size)
+ : subtype_(subtype), size_(size) {
+ assert(size_ > 1);
+ assert(size_ < 5);
+}
+
+VectorType::~VectorType() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/vector_type.h b/src/ast/type/vector_type.h
new file mode 100644
index 0000000..a773275
--- /dev/null
+++ b/src/ast/type/vector_type.h
@@ -0,0 +1,59 @@
+// Copyright 2020 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_AST_TYPE_VECTOR_TYPE_H_
+#define SRC_AST_TYPE_VECTOR_TYPE_H_
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A vector type.
+class VectorType : public Type {
+ public:
+ /// Constructor
+ /// @param subtype the vector element type
+ /// @param size the number of elements in the vector
+ VectorType(Type* subtype, size_t size);
+ /// Move constructor
+ VectorType(VectorType&&) = default;
+ ~VectorType() override;
+
+ /// @returns true if the type is a vector type
+ bool IsVector() const override { return true; }
+
+ /// @returns the type of the vector elements
+ Type* type() const { return subtype_; }
+ /// @returns the size of the vector
+ size_t size() const { return size_; }
+
+ /// @returns the name for th type
+ std::string type_name() const override {
+ return "__vec_" + std::to_string(size_) + subtype_->type_name();
+ }
+
+ private:
+ Type* subtype_ = nullptr;
+ size_t size_ = 2;
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_VECTOR_TYPE_H_
diff --git a/src/ast/type/vector_type_test.cc b/src/ast/type/vector_type_test.cc
new file mode 100644
index 0000000..bc08383
--- /dev/null
+++ b/src/ast/type/vector_type_test.cc
@@ -0,0 +1,56 @@
+// Copyright 2020 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/ast/type/vector_type.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+using VectorTypeTest = testing::Test;
+
+TEST_F(VectorTypeTest, Creation) {
+ I32Type i32;
+ VectorType v{&i32, 2};
+ EXPECT_EQ(v.type(), &i32);
+ EXPECT_EQ(v.size(), 2);
+}
+
+TEST_F(VectorTypeTest, Is) {
+ I32Type i32;
+ VectorType v{&i32, 4};
+ EXPECT_FALSE(v.IsAlias());
+ EXPECT_FALSE(v.IsArray());
+ EXPECT_FALSE(v.IsBool());
+ EXPECT_FALSE(v.IsF32());
+ EXPECT_FALSE(v.IsI32());
+ EXPECT_FALSE(v.IsMatrix());
+ EXPECT_FALSE(v.IsPointer());
+ EXPECT_FALSE(v.IsStruct());
+ EXPECT_FALSE(v.IsU32());
+ EXPECT_TRUE(v.IsVector());
+}
+
+TEST_F(VectorTypeTest, TypeName) {
+ I32Type i32;
+ VectorType v{&i32, 3};
+ EXPECT_EQ(v.type_name(), "__vec_3__i32");
+}
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/void_type.cc b/src/ast/type/void_type.cc
new file mode 100644
index 0000000..f41a9ba
--- /dev/null
+++ b/src/ast/type/void_type.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/ast/type/void_type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+VoidType::VoidType() = default;
+
+VoidType::~VoidType() = default;
+
+} // namespace type
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type/void_type.h b/src/ast/type/void_type.h
new file mode 100644
index 0000000..702a9b5
--- /dev/null
+++ b/src/ast/type/void_type.h
@@ -0,0 +1,46 @@
+// Copyright 2020 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_AST_TYPE_VOID_TYPE_H_
+#define SRC_AST_TYPE_VOID_TYPE_H_
+
+#include <string>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+namespace type {
+
+/// A void type
+class VoidType : public Type {
+ public:
+ /// Constructor
+ VoidType();
+ /// Move constructor
+ VoidType(VoidType&&) = default;
+ ~VoidType() override;
+
+ /// @returns true if the type is a void type
+ bool IsVoid() const override { return true; }
+
+ /// @returns the name for this type
+ std::string type_name() const override { return "__void"; }
+};
+
+} // namespace type
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_VOID_TYPE_H_
diff --git a/src/ast/type_initializer_expression.cc b/src/ast/type_initializer_expression.cc
new file mode 100644
index 0000000..0b092f3
--- /dev/null
+++ b/src/ast/type_initializer_expression.cc
@@ -0,0 +1,51 @@
+// Copyright 2020 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/ast/type_initializer_expression.h"
+
+namespace tint {
+namespace ast {
+
+TypeInitializerExpression::TypeInitializerExpression(
+ type::Type* type,
+ std::vector<std::unique_ptr<Expression>> values)
+ : InitializerExpression(), type_(type), values_(std::move(values)) {}
+
+TypeInitializerExpression::TypeInitializerExpression(
+ const Source& source,
+ type::Type* type,
+ std::vector<std::unique_ptr<Expression>> values)
+ : InitializerExpression(source), type_(type), values_(std::move(values)) {}
+
+TypeInitializerExpression::~TypeInitializerExpression() = default;
+
+bool TypeInitializerExpression::IsValid() const {
+ return values_.size() > 0;
+}
+
+void TypeInitializerExpression::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "TypeInitializer{" << std::endl;
+ make_indent(out, indent + 2);
+ out << type_->type_name() << std::endl;
+
+ for (const auto& val : values_) {
+ val->to_str(out, indent + 2);
+ }
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/type_initializer_expression.h b/src/ast/type_initializer_expression.h
new file mode 100644
index 0000000..9b7af2f
--- /dev/null
+++ b/src/ast/type_initializer_expression.h
@@ -0,0 +1,85 @@
+// Copyright 2020 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_AST_TYPE_INITIALIZER_EXPRESSION_H_
+#define SRC_AST_TYPE_INITIALIZER_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/initializer_expression.h"
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+
+/// A type specific initializer
+class TypeInitializerExpression : public InitializerExpression {
+ public:
+ /// Constructor
+ /// @param type the type
+ /// @param values the values
+ explicit TypeInitializerExpression(
+ type::Type* type,
+ std::vector<std::unique_ptr<Expression>> values);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param type the type
+ /// @param values the initializer values
+ TypeInitializerExpression(const Source& source,
+ type::Type* type,
+ std::vector<std::unique_ptr<Expression>> values);
+ /// Move constructor
+ TypeInitializerExpression(TypeInitializerExpression&&) = default;
+ ~TypeInitializerExpression() override;
+
+ /// @returns true if this is a type initializer
+ bool IsTypeInitializer() const override { return true; }
+
+ /// Set the type
+ /// @param type the type
+ void set_type(type::Type* type) { type_ = type; }
+ /// @returns the type
+ type::Type* type() const { return type_; }
+
+ /// Set the values
+ /// @param values the values
+ void set_values(std::vector<std::unique_ptr<Expression>> values) {
+ values_ = std::move(values);
+ }
+ /// @returns the values
+ const std::vector<std::unique_ptr<Expression>>& values() const {
+ return values_;
+ }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ TypeInitializerExpression(const TypeInitializerExpression&) = delete;
+
+ type::Type* type_;
+ std::vector<std::unique_ptr<Expression>> values_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_TYPE_INITIALIZER_EXPRESSION_H_
diff --git a/src/ast/uint_literal.cc b/src/ast/uint_literal.cc
new file mode 100644
index 0000000..273f9ca
--- /dev/null
+++ b/src/ast/uint_literal.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 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/ast/uint_literal.h"
+
+namespace tint {
+namespace ast {
+
+UintLiteral::UintLiteral(uint32_t value) : value_(value) {}
+
+UintLiteral::~UintLiteral() = default;
+
+std::string UintLiteral::to_str() const {
+ return std::to_string(value_);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/uint_literal.h b/src/ast/uint_literal.h
new file mode 100644
index 0000000..0b2394e
--- /dev/null
+++ b/src/ast/uint_literal.h
@@ -0,0 +1,49 @@
+// Copyright 2020 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_AST_UINT_LITERAL_H_
+#define SRC_AST_UINT_LITERAL_H_
+
+#include <string>
+
+#include "src/ast/literal.h"
+
+namespace tint {
+namespace ast {
+
+/// A uint literal
+class UintLiteral : public Literal {
+ public:
+ /// Constructor
+ /// @param value the uint literals value
+ explicit UintLiteral(uint32_t value);
+ ~UintLiteral() override;
+
+ /// @returns true if this is a uint literal
+ bool IsUint() const override { return true; }
+
+ /// @returns the uint literal value
+ uint32_t value() const { return value_; }
+
+ /// @returns the literal as a string
+ std::string to_str() const override;
+
+ private:
+ uint32_t value_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_UINT_LITERAL_H_
diff --git a/src/ast/uint_literal_test.cc b/src/ast/uint_literal_test.cc
new file mode 100644
index 0000000..04628c3
--- /dev/null
+++ b/src/ast/uint_literal_test.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 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/ast/uint_literal.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace ast {
+
+using UintLiteralTest = testing::Test;
+
+TEST_F(UintLiteralTest, Value) {
+ UintLiteral u{47};
+ ASSERT_TRUE(u.IsUint());
+ EXPECT_EQ(u.value(), 47);
+}
+
+TEST_F(UintLiteralTest, Is) {
+ UintLiteral u{42};
+ EXPECT_FALSE(u.IsBool());
+ EXPECT_FALSE(u.IsInt());
+ EXPECT_FALSE(u.IsFloat());
+ EXPECT_TRUE(u.IsUint());
+}
+
+TEST_F(UintLiteralTest, ToStr) {
+ UintLiteral i{42};
+
+ EXPECT_EQ(i.to_str(), "42");
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/unary_derivative.cc b/src/ast/unary_derivative.cc
new file mode 100644
index 0000000..82ae23c
--- /dev/null
+++ b/src/ast/unary_derivative.cc
@@ -0,0 +1,39 @@
+// Copyright 2020 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/ast/unary_derivative.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, UnaryDerivative mod) {
+ switch (mod) {
+ case UnaryDerivative::kDpdx: {
+ out << "dpdx";
+ break;
+ }
+ case UnaryDerivative::kDpdy: {
+ out << "dpdy";
+ break;
+ }
+ case UnaryDerivative::kFwidth: {
+ out << "fwidth";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/unary_derivative.h b/src/ast/unary_derivative.h
new file mode 100644
index 0000000..741834c
--- /dev/null
+++ b/src/ast/unary_derivative.h
@@ -0,0 +1,31 @@
+// Copyright 2020 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_AST_UNARY_DERIVATIVE_H_
+#define SRC_AST_UNARY_DERIVATIVE_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// The unary derivative
+enum class UnaryDerivative { kDpdx = 0, kDpdy, kFwidth };
+
+std::ostream& operator<<(std::ostream& out, UnaryDerivative mod);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_UNARY_DERIVATIVE_H_
diff --git a/src/ast/unary_derivative_expression.cc b/src/ast/unary_derivative_expression.cc
new file mode 100644
index 0000000..1ae9a86
--- /dev/null
+++ b/src/ast/unary_derivative_expression.cc
@@ -0,0 +1,52 @@
+// Copyright 2020 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/ast/unary_derivative_expression.h"
+
+namespace tint {
+namespace ast {
+
+UnaryDerivativeExpression::UnaryDerivativeExpression(
+ UnaryDerivative op,
+ DerivativeModifier mod,
+ std::unique_ptr<Expression> param)
+ : Expression(), op_(op), modifier_(mod), param_(std::move(param)) {}
+
+UnaryDerivativeExpression::UnaryDerivativeExpression(
+ const Source& source,
+ UnaryDerivative op,
+ DerivativeModifier mod,
+ std::unique_ptr<Expression> param)
+ : Expression(source), op_(op), modifier_(mod), param_(std::move(param)) {}
+
+UnaryDerivativeExpression::~UnaryDerivativeExpression() = default;
+
+bool UnaryDerivativeExpression::IsValid() const {
+ return true;
+}
+
+void UnaryDerivativeExpression::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "UnaryDerivative{" << std::endl;
+ make_indent(out, indent + 2);
+ out << op_ << std::endl;
+ make_indent(out, indent + 2);
+ out << modifier_ << std::endl;
+ param_->to_str(out, indent);
+ make_indent(out, indent);
+ out << "}";
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/unary_derivative_expression.h b/src/ast/unary_derivative_expression.h
new file mode 100644
index 0000000..f601286
--- /dev/null
+++ b/src/ast/unary_derivative_expression.h
@@ -0,0 +1,94 @@
+// Copyright 2020 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_AST_UNARY_DERIVATIVE_EXPRESSION_H_
+#define SRC_AST_UNARY_DERIVATIVE_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/derivative_modifier.h"
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/unary_derivative.h"
+
+namespace tint {
+namespace ast {
+
+/// A unary derivative expression
+class UnaryDerivativeExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param op the op
+ /// @param mod the derivative modifier
+ /// @param param the param
+ UnaryDerivativeExpression(UnaryDerivative op,
+ DerivativeModifier mod,
+ std::unique_ptr<Expression> param);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param op the op
+ /// @param mod the derivative modifier
+ /// @param param the param
+ UnaryDerivativeExpression(const Source& source,
+ UnaryDerivative op,
+ DerivativeModifier mod,
+ std::unique_ptr<Expression> param);
+ /// Move constructor
+ UnaryDerivativeExpression(UnaryDerivativeExpression&&) = default;
+ ~UnaryDerivativeExpression() override;
+
+ /// Sets the op
+ /// @param op the op
+ void set_op(UnaryDerivative op) { op_ = op; }
+ /// @returns the op
+ UnaryDerivative op() const { return op_; }
+
+ /// Sets the derivative modifier
+ /// @param mod the modifier
+ void set_modifier(DerivativeModifier mod) { modifier_ = mod; }
+ /// @returns the derivative modifier
+ DerivativeModifier modifier() const { return modifier_; }
+
+ /// Sets the param
+ /// @param param the param
+ void set_param(std::unique_ptr<Expression> param) {
+ param_ = std::move(param);
+ }
+ /// @returns the param
+ Expression* param() const { return param_.get(); }
+
+ /// @returns true if this is an as expression
+ bool IsUnaryDerivative() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ UnaryDerivativeExpression(const UnaryDerivativeExpression&) = delete;
+
+ UnaryDerivative op_ = UnaryDerivative::kDpdx;
+ DerivativeModifier modifier_ = DerivativeModifier::kNone;
+ std::unique_ptr<Expression> param_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_UNARY_DERIVATIVE_EXPRESSION_H_
diff --git a/src/ast/unary_method.cc b/src/ast/unary_method.cc
new file mode 100644
index 0000000..7ff4653
--- /dev/null
+++ b/src/ast/unary_method.cc
@@ -0,0 +1,59 @@
+// Copyright 2020 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/ast/unary_method.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, UnaryMethod mod) {
+ switch (mod) {
+ case UnaryMethod::kAny: {
+ out << "any";
+ break;
+ }
+ case UnaryMethod::kAll: {
+ out << "all";
+ break;
+ }
+ case UnaryMethod::kIsNan: {
+ out << "is_nan";
+ break;
+ }
+ case UnaryMethod::kIsInf: {
+ out << "is_inf";
+ break;
+ }
+ case UnaryMethod::kIsFinite: {
+ out << "is_finite";
+ break;
+ }
+ case UnaryMethod::kIsNormal: {
+ out << "is_normal";
+ break;
+ }
+ case UnaryMethod::kDot: {
+ out << "dot";
+ break;
+ }
+ case UnaryMethod::kOuterProduct: {
+ out << "outer_product";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/unary_method.h b/src/ast/unary_method.h
new file mode 100644
index 0000000..3f719ef
--- /dev/null
+++ b/src/ast/unary_method.h
@@ -0,0 +1,40 @@
+// Copyright 2020 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_AST_UNARY_METHOD_H_
+#define SRC_AST_UNARY_METHOD_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// The unary methods
+enum class UnaryMethod {
+ kAny = 0,
+ kAll,
+ kIsNan,
+ kIsInf,
+ kIsFinite,
+ kIsNormal,
+ kDot,
+ kOuterProduct
+};
+
+std::ostream& operator<<(std::ostream& out, UnaryMethod mod);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_UNARY_METHOD_H_
diff --git a/src/ast/unary_method_expression.cc b/src/ast/unary_method_expression.cc
new file mode 100644
index 0000000..058c683
--- /dev/null
+++ b/src/ast/unary_method_expression.cc
@@ -0,0 +1,52 @@
+// Copyright 2020 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/ast/unary_method_expression.h"
+
+namespace tint {
+namespace ast {
+
+UnaryMethodExpression::UnaryMethodExpression(
+ UnaryMethod op,
+ std::vector<std::unique_ptr<Expression>> params)
+ : Expression(), op_(op), params_(std::move(params)) {}
+
+UnaryMethodExpression::UnaryMethodExpression(
+ const Source& source,
+ UnaryMethod op,
+ std::vector<std::unique_ptr<Expression>> params)
+ : Expression(source), op_(op), params_(std::move(params)) {}
+
+UnaryMethodExpression::~UnaryMethodExpression() = default;
+
+bool UnaryMethodExpression::IsValid() const {
+ return true;
+}
+
+void UnaryMethodExpression::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+
+ out << "UnaryMethod{" << std::endl;
+ make_indent(out, indent + 2);
+ out << op_ << std::endl;
+ for (const auto& param : params_) {
+ param->to_str(out, indent + 2);
+ out << std::endl;
+ }
+ make_indent(out, indent);
+ out << "}";
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/unary_method_expression.h b/src/ast/unary_method_expression.h
new file mode 100644
index 0000000..806d190
--- /dev/null
+++ b/src/ast/unary_method_expression.h
@@ -0,0 +1,85 @@
+// Copyright 2020 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_AST_UNARY_METHOD_EXPRESSION_H_
+#define SRC_AST_UNARY_METHOD_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/unary_method.h"
+
+namespace tint {
+namespace ast {
+
+/// A unary method expression
+class UnaryMethodExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param op the op
+ /// @param params the params
+ UnaryMethodExpression(UnaryMethod op,
+ std::vector<std::unique_ptr<Expression>> params);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param op the op
+ /// @param params the params
+ UnaryMethodExpression(const Source& source,
+ UnaryMethod op,
+ std::vector<std::unique_ptr<Expression>> params);
+ /// Move constructor
+ UnaryMethodExpression(UnaryMethodExpression&&) = default;
+ ~UnaryMethodExpression() override;
+
+ /// Sets the op
+ /// @param op the op
+ void set_op(UnaryMethod op) { op_ = op; }
+ /// @returns the op
+ UnaryMethod op() const { return op_; }
+
+ /// Sets the params
+ /// @param params the parameters
+ void set_params(std::vector<std::unique_ptr<Expression>> params) {
+ params_ = std::move(params);
+ }
+ /// @returns the params
+ const std::vector<std::unique_ptr<Expression>>& params() const {
+ return params_;
+ }
+
+ /// @returns true if this is an as expression
+ bool IsUnaryMethod() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ UnaryMethodExpression(const UnaryMethodExpression&) = delete;
+
+ UnaryMethod op_ = UnaryMethod::kAny;
+ std::vector<std::unique_ptr<Expression>> params_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_UNARY_METHOD_EXPRESSION_H_
diff --git a/src/ast/unary_op.cc b/src/ast/unary_op.cc
new file mode 100644
index 0000000..5c8bba1
--- /dev/null
+++ b/src/ast/unary_op.cc
@@ -0,0 +1,35 @@
+// Copyright 2020 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/ast/unary_op.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, UnaryOp mod) {
+ switch (mod) {
+ case UnaryOp::kNegation: {
+ out << "negation";
+ break;
+ }
+ case UnaryOp::kNot: {
+ out << "not";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/unary_op.h b/src/ast/unary_op.h
new file mode 100644
index 0000000..8a6b9f7
--- /dev/null
+++ b/src/ast/unary_op.h
@@ -0,0 +1,31 @@
+// Copyright 2020 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_AST_UNARY_OP_H_
+#define SRC_AST_UNARY_OP_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// The unary op
+enum class UnaryOp { kNegation = 0, kNot };
+
+std::ostream& operator<<(std::ostream& out, UnaryOp mod);
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_UNARY_OP_H_
diff --git a/src/ast/unary_op_expression.cc b/src/ast/unary_op_expression.cc
new file mode 100644
index 0000000..da31e8e
--- /dev/null
+++ b/src/ast/unary_op_expression.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 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/ast/unary_op_expression.h"
+
+namespace tint {
+namespace ast {
+
+UnaryOpExpression::UnaryOpExpression(UnaryOp op,
+ std::unique_ptr<Expression> expr)
+ : Expression(), op_(op), expr_(std::move(expr)) {}
+
+UnaryOpExpression::UnaryOpExpression(const Source& source,
+ UnaryOp op,
+ std::unique_ptr<Expression> expr)
+ : Expression(source), op_(op), expr_(std::move(expr)) {}
+
+UnaryOpExpression::~UnaryOpExpression() = default;
+
+bool UnaryOpExpression::IsValid() const {
+ return expr_ != nullptr;
+}
+
+void UnaryOpExpression::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "UnaryOp{" << op_ << std::endl;
+ expr_->to_str(out, indent + 2);
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/unary_op_expression.h b/src/ast/unary_op_expression.h
new file mode 100644
index 0000000..b77c185
--- /dev/null
+++ b/src/ast/unary_op_expression.h
@@ -0,0 +1,79 @@
+// Copyright 2020 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_AST_UNARY_OP_EXPRESSION_H_
+#define SRC_AST_UNARY_OP_EXPRESSION_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/unary_op.h"
+
+namespace tint {
+namespace ast {
+
+/// A unary op expression
+class UnaryOpExpression : public Expression {
+ public:
+ /// Constructor
+ /// @param op the op
+ /// @param expr the expr
+ UnaryOpExpression(UnaryOp op, std::unique_ptr<Expression> expr);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param op the op
+ /// @param expr the expr
+ UnaryOpExpression(const Source& source,
+ UnaryOp op,
+ std::unique_ptr<Expression> expr);
+ /// Move constructor
+ UnaryOpExpression(UnaryOpExpression&&) = default;
+ ~UnaryOpExpression() override;
+
+ /// Sets the op
+ /// @param op the op
+ void set_op(UnaryOp op) { op_ = op; }
+ /// @returns the op
+ UnaryOp op() const { return op_; }
+
+ /// Sets the expr
+ /// @param expr the expression
+ void set_expr(std::unique_ptr<Expression> expr) { expr_ = std::move(expr); }
+ /// @returns the expression
+ Expression* expr() const { return expr_.get(); }
+
+ /// @returns true if this is an as expression
+ bool IsUnaryOp() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ UnaryOpExpression(const UnaryOpExpression&) = delete;
+
+ UnaryOp op_ = UnaryOp::kNegation;
+ std::unique_ptr<Expression> expr_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_UNARY_OP_EXPRESSION_H_
diff --git a/src/ast/unless_statement.cc b/src/ast/unless_statement.cc
new file mode 100644
index 0000000..7f591b0
--- /dev/null
+++ b/src/ast/unless_statement.cc
@@ -0,0 +1,56 @@
+// Copyright 2020 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/ast/unless_statement.h"
+
+namespace tint {
+namespace ast {
+
+UnlessStatement::UnlessStatement(std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(), condition_(std::move(condition)), body_(std::move(body)) {}
+
+UnlessStatement::UnlessStatement(const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body)
+ : Statement(source),
+ condition_(std::move(condition)),
+ body_(std::move(body)) {}
+
+UnlessStatement::~UnlessStatement() = default;
+
+bool UnlessStatement::IsValid() const {
+ return condition_ != nullptr;
+}
+
+void UnlessStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Unless{" << std::endl;
+
+ condition_->to_str(out, indent + 2);
+ make_indent(out, indent);
+ out << "{" << std::endl;
+
+ for (const auto& stmt : body_)
+ stmt->to_str(out, indent + 4);
+
+ make_indent(out, indent + 2);
+ out << "}";
+
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/unless_statement.h b/src/ast/unless_statement.h
new file mode 100644
index 0000000..bafbb43
--- /dev/null
+++ b/src/ast/unless_statement.h
@@ -0,0 +1,84 @@
+// Copyright 2020 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_AST_UNLESS_STATEMENT_H_
+#define SRC_AST_UNLESS_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// A unless statement
+class UnlessStatement : public Statement {
+ public:
+ /// Constructor
+ /// @param condition the condition expression
+ /// @param body the body statements
+ UnlessStatement(std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param condition the condition expression
+ /// @param body the body statements
+ UnlessStatement(const Source& source,
+ std::unique_ptr<Expression> condition,
+ std::vector<std::unique_ptr<Statement>> body);
+ /// Move constructor
+ UnlessStatement(UnlessStatement&&) = default;
+ ~UnlessStatement() override;
+
+ /// Sets the condition expression
+ /// @param condition the condition expression
+ void set_condition(std::unique_ptr<Expression> condition) {
+ condition_ = std::move(condition);
+ }
+ /// @returns the condition statements
+ Expression* condition() const { return condition_.get(); }
+
+ /// Sets the body statements
+ /// @param body the body statements
+ void set_body(std::vector<std::unique_ptr<Statement>> body) {
+ body_ = std::move(body);
+ }
+ /// @returns the body statements
+ const std::vector<std::unique_ptr<Statement>>& body() const { return body_; }
+
+ /// @returns true if this is an unless statement
+ bool IsUnless() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ UnlessStatement(const UnlessStatement&) = delete;
+
+ std::unique_ptr<Expression> condition_;
+ std::vector<std::unique_ptr<Statement>> body_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_UNLESS_STATEMENT_H_
diff --git a/src/ast/variable.cc b/src/ast/variable.cc
new file mode 100644
index 0000000..1caa64b
--- /dev/null
+++ b/src/ast/variable.cc
@@ -0,0 +1,74 @@
+// Copyright 2020 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/ast/variable.h"
+
+#include <assert.h>
+
+#include "src/ast/decorated_variable.h"
+
+namespace tint {
+namespace ast {
+
+Variable::Variable(const std::string& name, StorageClass sc, type::Type* type)
+ : Node(), name_(name), storage_class_(sc), type_(type) {}
+
+Variable::Variable(const Source& source,
+ const std::string& name,
+ StorageClass sc,
+ type::Type* type)
+ : Node(source), name_(name), storage_class_(sc), type_(type) {}
+
+Variable::~Variable() = default;
+
+DecoratedVariable* Variable::AsDecorated() {
+ assert(IsDecorated());
+ return static_cast<DecoratedVariable*>(this);
+}
+
+bool Variable::IsValid() const {
+ if (name_.length() == 0) {
+ return false;
+ }
+ if (type_ == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+void Variable::info_to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << name_ << std::endl;
+ make_indent(out, indent);
+ out << storage_class_ << std::endl;
+ make_indent(out, indent);
+ out << type_->type_name() << std::endl;
+}
+
+void Variable::to_str(std::ostream& out, size_t indent) const {
+ info_to_str(out, indent);
+
+ if (initializer_ != nullptr) {
+ make_indent(out, indent);
+ out << "{" << std::endl;
+
+ initializer_->to_str(out, indent + 2);
+
+ make_indent(out, indent);
+ out << "}" << std::endl;
+ }
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/variable.h b/src/ast/variable.h
new file mode 100644
index 0000000..db27ed2
--- /dev/null
+++ b/src/ast/variable.h
@@ -0,0 +1,122 @@
+// Copyright 2020 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_AST_VARIABLE_H_
+#define SRC_AST_VARIABLE_H_
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/node.h"
+#include "src/ast/storage_class.h"
+#include "src/ast/type/type.h"
+
+namespace tint {
+namespace ast {
+
+class DecoratedVariable;
+
+/// A Variable statement.
+class Variable : public Node {
+ public:
+ /// Create a new empty variable statement
+ Variable() = default;
+ /// Create a variable
+ /// @param name the variables name
+ /// @param sc the variable storage class
+ /// @param type the variables type
+ Variable(const std::string& name, StorageClass sc, type::Type* type);
+ /// Create a variable
+ /// @param source the variable source
+ /// @param name the variables name
+ /// @param sc the variable storage class
+ /// @param type the variables type
+ Variable(const Source& source,
+ const std::string& name,
+ StorageClass sc,
+ type::Type* type);
+ /// Move constructor
+ Variable(Variable&&) = default;
+
+ ~Variable() override;
+
+ /// Sets the variable name
+ /// @param name the name to set
+ void set_name(const std::string& name) { name_ = name; }
+ /// @returns the variable name
+ const std::string& name() { return name_; }
+
+ /// Sets the type of the variable
+ /// @param type the type
+ void set_type(type::Type* type) { type_ = type; }
+ /// @returns the variables type.
+ type::Type* type() const { return type_; }
+
+ /// Sets the storage class
+ /// @param sc the storage class
+ void set_storage_class(StorageClass sc) { storage_class_ = sc; }
+ /// @returns the storage class
+ StorageClass storage_class() const { return storage_class_; }
+
+ /// Sets the initializer
+ /// @param expr the initializer expression
+ void set_initializer(std::unique_ptr<Expression> expr) {
+ initializer_ = std::move(expr);
+ }
+ /// @returns the initializer expression or nullptr if none set
+ Expression* initializer() const { return initializer_.get(); }
+
+ /// Sets if the variable is constant
+ /// @param val the value to be set
+ void set_is_const(bool val) { is_const_ = val; }
+ /// @returns true if this is a constant, false otherwise
+ bool is_const() const { return is_const_; }
+
+ /// @returns true if this is a decorated variable
+ virtual bool IsDecorated() const { return false; }
+
+ /// @returns the expression as a decorated variable
+ DecoratedVariable* AsDecorated();
+
+ /// @returns true if the name and path are both present
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ protected:
+ /// Output information for this variable.
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void info_to_str(std::ostream& out, size_t indent) const;
+
+ private:
+ Variable(const Variable&) = delete;
+
+ bool is_const_ = false;
+ std::string name_;
+ StorageClass storage_class_ = StorageClass::kNone;
+ type::Type* type_ = nullptr;
+ std::unique_ptr<Expression> initializer_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_VARIABLE_H_
diff --git a/src/ast/variable_decoration.cc b/src/ast/variable_decoration.cc
new file mode 100644
index 0000000..2f70268
--- /dev/null
+++ b/src/ast/variable_decoration.cc
@@ -0,0 +1,52 @@
+// Copyright 2020 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/ast/variable_decoration.h"
+
+#include <assert.h>
+
+#include "src/ast/binding_decoration.h"
+#include "src/ast/builtin_decoration.h"
+#include "src/ast/location_decoration.h"
+#include "src/ast/set_decoration.h"
+
+namespace tint {
+namespace ast {
+
+VariableDecoration::VariableDecoration() = default;
+
+VariableDecoration::~VariableDecoration() = default;
+
+BindingDecoration* VariableDecoration::AsBinding() {
+ assert(IsBinding());
+ return static_cast<BindingDecoration*>(this);
+}
+
+BuiltinDecoration* VariableDecoration::AsBuiltin() {
+ assert(IsBuiltin());
+ return static_cast<BuiltinDecoration*>(this);
+}
+
+LocationDecoration* VariableDecoration::AsLocation() {
+ assert(IsLocation());
+ return static_cast<LocationDecoration*>(this);
+}
+
+SetDecoration* VariableDecoration::AsSet() {
+ assert(IsSet());
+ return static_cast<SetDecoration*>(this);
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/variable_decoration.h b/src/ast/variable_decoration.h
new file mode 100644
index 0000000..2e7bd04
--- /dev/null
+++ b/src/ast/variable_decoration.h
@@ -0,0 +1,63 @@
+// Copyright 2020 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_AST_VARIABLE_DECORATION_H_
+#define SRC_AST_VARIABLE_DECORATION_H_
+
+#include <ostream>
+#include <string>
+
+namespace tint {
+namespace ast {
+
+class BindingDecoration;
+class BuiltinDecoration;
+class LocationDecoration;
+class SetDecoration;
+
+/// A decoration attached to a variable
+class VariableDecoration {
+ public:
+ virtual ~VariableDecoration();
+
+ /// @returns true if this is a binding decoration
+ virtual bool IsBinding() const { return false; }
+ /// @returns true if this is a builtin decoration
+ virtual bool IsBuiltin() const { return false; }
+ /// @returns true if this is a location decoration
+ virtual bool IsLocation() const { return false; }
+ /// @returns true if this is a set decoration
+ virtual bool IsSet() const { return false; }
+
+ /// @returns the decoration as a binding decoration
+ BindingDecoration* AsBinding();
+ /// @returns the decoration as a builtin decoration
+ BuiltinDecoration* AsBuiltin();
+ /// @returns the decoration as a location decoration
+ LocationDecoration* AsLocation();
+ /// @returns the decoration as a set decoration
+ SetDecoration* AsSet();
+
+ /// Outputs the variable decoration to the given stream
+ /// @param out the stream to output too
+ virtual void to_str(std::ostream& out) const = 0;
+
+ protected:
+ VariableDecoration();
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_VARIABLE_DECORATION_H_
diff --git a/src/ast/variable_statement.cc b/src/ast/variable_statement.cc
new file mode 100644
index 0000000..a195575
--- /dev/null
+++ b/src/ast/variable_statement.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 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/ast/variable_statement.h"
+
+namespace tint {
+namespace ast {
+
+VariableStatement::VariableStatement(std::unique_ptr<Variable> variable)
+ : Statement(), variable_(std::move(variable)) {}
+
+VariableStatement::VariableStatement(const Source& source,
+ std::unique_ptr<Variable> variable)
+ : Statement(source), variable_(std::move(variable)) {}
+
+VariableStatement::~VariableStatement() = default;
+
+bool VariableStatement::IsValid() const {
+ return variable_ != nullptr;
+}
+
+void VariableStatement::to_str(std::ostream& out, size_t indent) const {
+ make_indent(out, indent);
+ out << "Variable{" << std::endl;
+ variable_->to_str(out, indent + 2);
+ make_indent(out, indent);
+ out << "}" << std::endl;
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/ast/variable_statement.h b/src/ast/variable_statement.h
new file mode 100644
index 0000000..17e0742
--- /dev/null
+++ b/src/ast/variable_statement.h
@@ -0,0 +1,70 @@
+// Copyright 2020 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_AST_VARIABLE_STATEMENT_H_
+#define SRC_AST_VARIABLE_STATEMENT_H_
+
+#include <memory>
+#include <utility>
+
+#include "src/ast/expression.h"
+#include "src/ast/statement.h"
+#include "src/ast/variable.h"
+
+namespace tint {
+namespace ast {
+
+/// A variable statement
+class VariableStatement : public Statement {
+ public:
+ /// Constructor
+ /// @param variable the variable
+ explicit VariableStatement(std::unique_ptr<Variable> variable);
+ /// Constructor
+ /// @param source the initializer source
+ /// @param variable the variable
+ VariableStatement(const Source& source, std::unique_ptr<Variable> variable);
+ /// Move constructor
+ VariableStatement(VariableStatement&&) = default;
+ ~VariableStatement() override;
+
+ /// Sets the variable
+ /// @param variable the variable to set
+ void set_variable(std::unique_ptr<Variable> variable) {
+ variable_ = std::move(variable);
+ }
+ /// @returns the variable
+ Variable* variable() const { return variable_.get(); }
+
+ /// @returns true if this is an variable statement
+ bool IsVariable() const override { return true; }
+
+ /// @returns true if the node is valid
+ bool IsValid() const override;
+
+ /// Writes a representation of the node to the output stream
+ /// @param out the stream to write to
+ /// @param indent number of spaces to indent the node when writing
+ void to_str(std::ostream& out, size_t indent) const override;
+
+ private:
+ VariableStatement(const VariableStatement&) = delete;
+
+ std::unique_ptr<Variable> variable_;
+};
+
+} // namespace ast
+} // namespace tint
+
+#endif // SRC_AST_VARIABLE_STATEMENT_H_
diff --git a/src/ast/variable_test.cc b/src/ast/variable_test.cc
new file mode 100644
index 0000000..0f4f59b
--- /dev/null
+++ b/src/ast/variable_test.cc
@@ -0,0 +1,100 @@
+// Copyright 2020 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/ast/variable.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+
+namespace tint {
+namespace ast {
+
+using VariableTest = testing::Test;
+
+TEST_F(VariableTest, Creation) {
+ type::I32Type t;
+ Variable v("my_var", StorageClass::kFunction, &t);
+
+ EXPECT_EQ(v.name(), "my_var");
+ EXPECT_EQ(v.storage_class(), StorageClass::kFunction);
+ EXPECT_EQ(v.type(), &t);
+ EXPECT_EQ(v.line(), 0);
+ EXPECT_EQ(v.column(), 0);
+}
+
+TEST_F(VariableTest, CreationWithSource) {
+ Source s{27, 4};
+ type::F32Type t;
+ Variable v(s, "i", StorageClass::kPrivate, &t);
+
+ EXPECT_EQ(v.name(), "i");
+ EXPECT_EQ(v.storage_class(), StorageClass::kPrivate);
+ EXPECT_EQ(v.type(), &t);
+ EXPECT_EQ(v.line(), 27);
+ EXPECT_EQ(v.column(), 4);
+}
+
+TEST_F(VariableTest, CreationEmpty) {
+ Source s{27, 4};
+ Variable v;
+ v.set_source(s);
+ v.set_storage_class(StorageClass::kWorkgroup);
+ v.set_name("a_var");
+
+ type::I32Type t;
+ v.set_type(&t);
+
+ EXPECT_EQ(v.name(), "a_var");
+ EXPECT_EQ(v.storage_class(), StorageClass::kWorkgroup);
+ EXPECT_EQ(v.type(), &t);
+ EXPECT_EQ(v.line(), 27);
+ EXPECT_EQ(v.column(), 4);
+}
+
+TEST_F(VariableTest, IsValid) {
+ type::I32Type t;
+ Variable v{"my_var", StorageClass::kNone, &t};
+ EXPECT_TRUE(v.IsValid());
+}
+
+TEST_F(VariableTest, IsValid_MissinName) {
+ type::I32Type t;
+ Variable v{"", StorageClass::kNone, &t};
+ EXPECT_FALSE(v.IsValid());
+}
+
+TEST_F(VariableTest, IsValid_MissingType) {
+ Variable v{"x", StorageClass::kNone, nullptr};
+ EXPECT_FALSE(v.IsValid());
+}
+
+TEST_F(VariableTest, IsValid_MissingBoth) {
+ Variable v;
+ EXPECT_FALSE(v.IsValid());
+}
+
+TEST_F(VariableTest, to_str) {
+ type::F32Type t;
+ Variable v{"my_var", StorageClass::kFunction, &t};
+ std::ostringstream out;
+ v.to_str(out, 0);
+ EXPECT_EQ(out.str(), R"(my_var
+function
+__f32
+)");
+}
+
+} // namespace ast
+} // namespace tint
diff --git a/src/reader/reader.cc b/src/reader/reader.cc
new file mode 100644
index 0000000..6d90273
--- /dev/null
+++ b/src/reader/reader.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 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/reader/reader.h"
+
+namespace tint {
+namespace reader {
+
+Reader::Reader() = default;
+
+Reader::~Reader() = default;
+
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/reader.h b/src/reader/reader.h
new file mode 100644
index 0000000..8aa48d5
--- /dev/null
+++ b/src/reader/reader.h
@@ -0,0 +1,57 @@
+// Copyright 2020 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_READER_READER_H_
+#define SRC_READER_READER_H_
+
+#include <string>
+
+#include "src/ast/module.h"
+
+namespace tint {
+namespace reader {
+
+/// Base class for input readers
+class Reader {
+ public:
+ virtual ~Reader();
+
+ /// Parses the input data
+ /// @returns true if the parse was successful
+ virtual bool Parse() = 0;
+
+ /// @returns true if an error was encountered
+ bool has_error() const { return error_.size() > 0; }
+ /// @returns the parser error string
+ const std::string& error() const { return error_; }
+
+ /// @returns the module. The module in the parser will be reset after this.
+ virtual ast::Module module() = 0;
+
+ protected:
+ /// Constructor
+ Reader();
+
+ /// Sets the error string
+ /// @param msg the error message
+ void set_error(const std::string& msg) { error_ = msg; }
+
+ /// An error message, if an error was encountered
+ std::string error_;
+};
+
+} // namespace reader
+} // namespace tint
+
+#endif // SRC_READER_READER_H_
diff --git a/src/reader/spv/parser.cc b/src/reader/spv/parser.cc
new file mode 100644
index 0000000..3c56d39
--- /dev/null
+++ b/src/reader/spv/parser.cc
@@ -0,0 +1,37 @@
+// Copyright 2020 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/reader/spv/parser.h"
+
+#include <utility>
+
+namespace tint {
+namespace reader {
+namespace spv {
+
+Parser::Parser(const std::vector<uint32_t>&) : Reader() {}
+
+Parser::~Parser() = default;
+
+bool Parser::Parse() {
+ return false;
+}
+
+ast::Module Parser::module() {
+ return std::move(module_);
+}
+
+} // namespace spv
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/spv/parser.h b/src/reader/spv/parser.h
new file mode 100644
index 0000000..c47d14f
--- /dev/null
+++ b/src/reader/spv/parser.h
@@ -0,0 +1,51 @@
+// Copyright 2020 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_READER_SPV_PARSER_H_
+#define SRC_READER_SPV_PARSER_H_
+
+#include <vector>
+
+#include "src/reader/reader.h"
+
+namespace tint {
+namespace reader {
+namespace spv {
+
+class ParserImpl;
+
+/// Parser for SPIR-V source data
+class Parser : public Reader {
+ public:
+ /// Creates a new parser
+ /// @param input the input data to parse
+ explicit Parser(const std::vector<uint32_t>& input);
+ ~Parser() override;
+
+ /// Run the parser
+ /// @returns true if the parse was successful, false otherwise.
+ bool Parse() override;
+
+ /// @returns the module. The module in the parser will be reset after this.
+ ast::Module module() override;
+
+ private:
+ ast::Module module_;
+};
+
+} // namespace spv
+} // namespace reader
+} // namespace tint
+
+#endif // SRC_READER_SPV_PARSER_H_
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
new file mode 100644
index 0000000..903f9b1
--- /dev/null
+++ b/src/reader/wgsl/lexer.cc
@@ -0,0 +1,691 @@
+// Copyright 2020 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/reader/wgsl/lexer.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <limits>
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+namespace {
+
+bool is_whitespace(char c) {
+ return std::isspace(c);
+}
+
+} // namespace
+
+Lexer::Lexer(const std::string& input)
+ : input_(input), len_(static_cast<uint32_t>(input.size())) {}
+
+Lexer::~Lexer() = default;
+
+Token Lexer::next() {
+ skip_whitespace();
+ skip_comments();
+
+ if (is_eof()) {
+ return {Token::Type::kEOF, make_source()};
+ }
+
+ auto t = try_hex_integer();
+ if (!t.IsUninitialized()) {
+ return t;
+ }
+
+ t = try_float();
+ if (!t.IsUninitialized()) {
+ return t;
+ }
+
+ t = try_integer();
+ if (!t.IsUninitialized()) {
+ return t;
+ }
+
+ t = try_string();
+ if (!t.IsUninitialized()) {
+ return t;
+ }
+
+ t = try_punctuation();
+ if (!t.IsUninitialized()) {
+ return t;
+ }
+
+ t = try_ident();
+ if (!t.IsUninitialized()) {
+ return t;
+ }
+
+ return {Token::Type::kError, make_source(), "invalid character found"};
+}
+
+Source Lexer::make_source() const {
+ return Source{line_, column_};
+}
+
+bool Lexer::is_eof() const {
+ return pos_ >= len_;
+}
+
+bool Lexer::is_alpha(char ch) const {
+ return std::isalpha(ch) || ch == '_';
+}
+
+bool Lexer::is_digit(char ch) const {
+ return std::isdigit(ch);
+}
+
+bool Lexer::is_alphanum(char ch) const {
+ return is_alpha(ch) || is_digit(ch);
+}
+
+bool Lexer::is_hex(char ch) const {
+ return std::isxdigit(ch);
+}
+
+bool Lexer::matches(size_t pos, const std::string& substr) {
+ if (pos >= input_.size())
+ return false;
+ return input_.substr(pos, substr.size()) == substr;
+}
+
+void Lexer::skip_whitespace() {
+ for (;;) {
+ auto pos = pos_;
+ while (!is_eof() && is_whitespace(input_[pos_])) {
+ if (matches(pos_, "\n")) {
+ pos_++;
+ line_++;
+ column_ = 1;
+ continue;
+ }
+
+ pos_++;
+ column_++;
+ }
+
+ skip_comments();
+
+ // If the cursor didn't advance we didn't remove any whitespace
+ // so we're done.
+ if (pos == pos_)
+ break;
+ }
+}
+
+void Lexer::skip_comments() {
+ if (!matches(pos_, "#")) {
+ return;
+ }
+
+ while (!is_eof() && !matches(pos_, "\n")) {
+ pos_++;
+ column_++;
+ }
+}
+
+Token Lexer::try_float() {
+ auto start = pos_;
+ auto end = pos_;
+
+ auto source = make_source();
+
+ if (matches(end, "-")) {
+ end++;
+ }
+ while (end < len_ && is_digit(input_[end])) {
+ end++;
+ }
+
+ if (end >= len_ || !matches(end, ".")) {
+ return {};
+ }
+ end++;
+
+ while (end < len_ && is_digit(input_[end])) {
+ end++;
+ }
+
+ // Parse the exponent if one exists
+ if (end < len_ && matches(end, "e")) {
+ end++;
+ if (end < len_ && (matches(end, "+") || matches(end, "-"))) {
+ end++;
+ }
+
+ auto exp_start = end;
+ while (end < len_ && isdigit(input_[end])) {
+ end++;
+ }
+
+ // Must have an exponent
+ if (exp_start == end)
+ return {};
+ }
+
+ auto str = input_.substr(start, end - start);
+ if (str == "." || str == "-.")
+ return {};
+
+ pos_ = end;
+ column_ += (end - start);
+
+ auto res = strtod(input_.c_str() + start, nullptr);
+ // This handles if the number is a really small in the exponent
+ if (res > 0 && res < static_cast<double>(std::numeric_limits<float>::min())) {
+ return {Token::Type::kError, source, "f32 (" + str + " too small"};
+ }
+ // This handles if the number is really large negative number
+ if (res < static_cast<double>(std::numeric_limits<float>::lowest())) {
+ return {Token::Type::kError, source, "f32 (" + str + ") too small"};
+ }
+ if (res > static_cast<double>(std::numeric_limits<float>::max())) {
+ return {Token::Type::kError, source, "f32 (" + str + ") too large"};
+ }
+
+ return {source, static_cast<float>(res)};
+}
+
+Token Lexer::build_token_from_int_if_possible(const Source& source,
+ size_t start,
+ size_t end,
+ int32_t base) {
+ auto res = strtoll(input_.c_str() + start, nullptr, base);
+ if (matches(pos_, "u")) {
+ if (static_cast<uint64_t>(res) >
+ static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())) {
+ return {Token::Type::kError, source,
+ "u32 (" + input_.substr(start, end - start) + ") too large"};
+ }
+ return {source, static_cast<uint32_t>(res)};
+ }
+
+ if (res < static_cast<int64_t>(std::numeric_limits<int32_t>::min())) {
+ return {Token::Type::kError, source,
+ "i32 (" + input_.substr(start, end - start) + ") too small"};
+ }
+ if (res > static_cast<int64_t>(std::numeric_limits<int32_t>::max())) {
+ return {Token::Type::kError, source,
+ "i32 (" + input_.substr(start, end - start) + ") too large"};
+ }
+ return {source, static_cast<int32_t>(res)};
+}
+
+Token Lexer::try_hex_integer() {
+ auto start = pos_;
+ auto end = pos_;
+
+ auto source = make_source();
+
+ if (matches(end, "-")) {
+ end++;
+ }
+ if (!matches(end, "0x")) {
+ return Token();
+ }
+ end += 2;
+
+ while (!is_eof() && is_hex(input_[end])) {
+ end += 1;
+ }
+
+ pos_ = end;
+ column_ += (end - start);
+
+ return build_token_from_int_if_possible(source, start, end, 16);
+}
+
+Token Lexer::try_integer() {
+ auto start = pos_;
+ auto end = start;
+
+ auto source = make_source();
+
+ if (matches(end, "-")) {
+ end++;
+ }
+ if (end >= len_ || !is_digit(input_[end])) {
+ return {};
+ }
+
+ auto first = end;
+ while (end < len_ && is_digit(input_[end])) {
+ end++;
+ }
+
+ // If the first digit is a zero this must only be zero as leading zeros
+ // are not allowed.
+ if (input_[first] == '0' && (end - first != 1))
+ return {};
+
+ pos_ = end;
+ column_ += (end - start);
+
+ return build_token_from_int_if_possible(source, start, end, 10);
+}
+
+Token Lexer::try_ident() {
+ // Must begin with an a-zA-Z_
+ if (!is_alpha(input_[pos_])) {
+ return {};
+ }
+
+ auto source = make_source();
+
+ auto s = pos_;
+ while (!is_eof() && is_alphanum(input_[pos_])) {
+ pos_++;
+ column_++;
+ }
+
+ auto str = input_.substr(s, pos_ - s);
+ auto t = check_reserved(source, str);
+ if (!t.IsUninitialized()) {
+ return t;
+ }
+
+ t = check_keyword(source, str);
+ if (!t.IsUninitialized()) {
+ return t;
+ }
+
+ return {Token::Type::kIdentifier, source, str};
+}
+
+Token Lexer::try_string() {
+ if (!matches(pos_, R"(")"))
+ return {};
+
+ auto source = make_source();
+
+ pos_++;
+ auto start = pos_;
+ while (pos_ < len_ && !matches(pos_, R"(")")) {
+ pos_++;
+ }
+ auto end = pos_;
+ pos_++;
+ column_ += (pos_ - start) + 1;
+
+ return {Token::Type::kStringLiteral, source,
+ input_.substr(start, end - start)};
+}
+
+Token Lexer::try_punctuation() {
+ auto source = make_source();
+ auto type = Token::Type::kUninitialized;
+
+ if (matches(pos_, "[[")) {
+ type = Token::Type::kAttrLeft;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, "]]")) {
+ type = Token::Type::kAttrRight;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, "(")) {
+ type = Token::Type::kParenLeft;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, ")")) {
+ type = Token::Type::kParenRight;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "[")) {
+ type = Token::Type::kBraceLeft;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "]")) {
+ type = Token::Type::kBraceRight;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "{")) {
+ type = Token::Type::kBracketLeft;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "}")) {
+ type = Token::Type::kBracketRight;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "&&")) {
+ type = Token::Type::kAndAnd;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, "&")) {
+ type = Token::Type::kAnd;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "/")) {
+ type = Token::Type::kForwardSlash;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "!=")) {
+ type = Token::Type::kNotEqual;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, "!")) {
+ type = Token::Type::kBang;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "::")) {
+ type = Token::Type::kNamespace;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, ":")) {
+ type = Token::Type::kColon;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, ",")) {
+ type = Token::Type::kComma;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "==")) {
+ type = Token::Type::kEqualEqual;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, "=")) {
+ type = Token::Type::kEqual;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, ">=")) {
+ type = Token::Type::kGreaterThanEqual;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, ">")) {
+ type = Token::Type::kGreaterThan;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "<=")) {
+ type = Token::Type::kLessThanEqual;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, "<")) {
+ type = Token::Type::kLessThan;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "%")) {
+ type = Token::Type::kMod;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "->")) {
+ type = Token::Type::kArrow;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, "-")) {
+ type = Token::Type::kMinus;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, ".")) {
+ type = Token::Type::kPeriod;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "+")) {
+ type = Token::Type::kPlus;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "||")) {
+ type = Token::Type::kOrOr;
+ pos_ += 2;
+ column_ += 2;
+ } else if (matches(pos_, "|")) {
+ type = Token::Type::kOr;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, ";")) {
+ type = Token::Type::kSemicolon;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "*")) {
+ type = Token::Type::kStar;
+ pos_ += 1;
+ column_ += 1;
+ } else if (matches(pos_, "^")) {
+ type = Token::Type::kXor;
+ pos_ += 1;
+ column_ += 1;
+ }
+
+ return {type, source};
+}
+
+Token Lexer::check_keyword(const Source& source, const std::string& str) {
+ if (str == "all")
+ return {Token::Type::kAll, source, "all"};
+ if (str == "any")
+ return {Token::Type::kAny, source, "any"};
+ if (str == "array")
+ return {Token::Type::kArray, source, "array"};
+ if (str == "as")
+ return {Token::Type::kAs, source, "as"};
+ if (str == "binding")
+ return {Token::Type::kBinding, source, "binding"};
+ if (str == "block")
+ return {Token::Type::kBlock, source, "block"};
+ if (str == "bool")
+ return {Token::Type::kBool, source, "bool"};
+ if (str == "break")
+ return {Token::Type::kBreak, source, "break"};
+ if (str == "builtin")
+ return {Token::Type::kBuiltin, source, "builtin"};
+ if (str == "case")
+ return {Token::Type::kCase, source, "case"};
+ if (str == "cast")
+ return {Token::Type::kCast, source, "cast"};
+ if (str == "compute")
+ return {Token::Type::kCompute, source, "compute"};
+ if (str == "const")
+ return {Token::Type::kConst, source, "const"};
+ if (str == "continue")
+ return {Token::Type::kContinue, source, "continue"};
+ if (str == "continuing")
+ return {Token::Type::kContinuing, source, "continuing"};
+ if (str == "coarse")
+ return {Token::Type::kCoarse, source, "coarse"};
+ if (str == "default")
+ return {Token::Type::kDefault, source, "default"};
+ if (str == "dot")
+ return {Token::Type::kDot, source, "dot"};
+ if (str == "dpdx")
+ return {Token::Type::kDpdx, source, "dpdx"};
+ if (str == "dpdy")
+ return {Token::Type::kDpdy, source, "dpdy"};
+ if (str == "else")
+ return {Token::Type::kElse, source, "else"};
+ if (str == "elseif")
+ return {Token::Type::kElseIf, source, "elseif"};
+ if (str == "entry_point")
+ return {Token::Type::kEntryPoint, source, "entry_point"};
+ if (str == "f32")
+ return {Token::Type::kF32, source, "f32"};
+ if (str == "fallthrough")
+ return {Token::Type::kFallthrough, source, "fallthrough"};
+ if (str == "false")
+ return {Token::Type::kFalse, source, "false"};
+ if (str == "fine")
+ return {Token::Type::kFine, source, "fine"};
+ if (str == "fn")
+ return {Token::Type::kFn, source, "fn"};
+ if (str == "frag_coord")
+ return {Token::Type::kFragCoord, source, "frag_coord"};
+ if (str == "frag_depth")
+ return {Token::Type::kFragDepth, source, "frag_depth"};
+ if (str == "fragment")
+ return {Token::Type::kFragment, source, "fragment"};
+ if (str == "front_facing")
+ return {Token::Type::kFrontFacing, source, "front_facing"};
+ if (str == "function")
+ return {Token::Type::kFunction, source, "function"};
+ if (str == "fwidth")
+ return {Token::Type::kFwidth, source, "fwidth"};
+ if (str == "global_invocation_id")
+ return {Token::Type::kGlobalInvocationId, source, "global_invocation_id"};
+ if (str == "i32")
+ return {Token::Type::kI32, source, "i32"};
+ if (str == "if")
+ return {Token::Type::kIf, source, "if"};
+ if (str == "image")
+ return {Token::Type::kImage, source, "image"};
+ if (str == "import")
+ return {Token::Type::kImport, source, "import"};
+ if (str == "in")
+ return {Token::Type::kIn, source, "in"};
+ if (str == "instance_idx")
+ return {Token::Type::kInstanceIdx, source, "instance_idx"};
+ if (str == "is_nan")
+ return {Token::Type::kIsNan, source, "is_nan"};
+ if (str == "is_inf")
+ return {Token::Type::kIsInf, source, "is_inf"};
+ if (str == "is_finite")
+ return {Token::Type::kIsFinite, source, "is_finite"};
+ if (str == "is_normal")
+ return {Token::Type::kIsNormal, source, "is_normal"};
+ if (str == "kill")
+ return {Token::Type::kKill, source, "kill"};
+ if (str == "local_invocation_id")
+ return {Token::Type::kLocalInvocationId, source, "local_invocation_id"};
+ if (str == "local_invocation_idx")
+ return {Token::Type::kLocalInvocationIdx, source, "local_invocation_idx"};
+ if (str == "location")
+ return {Token::Type::kLocation, source, "location"};
+ if (str == "loop")
+ return {Token::Type::kLoop, source, "loop"};
+ if (str == "mat2x2")
+ return {Token::Type::kMat2x2, source, "mat2x2"};
+ if (str == "mat2x3")
+ return {Token::Type::kMat2x3, source, "mat2x3"};
+ if (str == "mat2x4")
+ return {Token::Type::kMat2x4, source, "mat2x4"};
+ if (str == "mat3x2")
+ return {Token::Type::kMat3x2, source, "mat3x2"};
+ if (str == "mat3x3")
+ return {Token::Type::kMat3x3, source, "mat3x3"};
+ if (str == "mat3x4")
+ return {Token::Type::kMat3x4, source, "mat3x4"};
+ if (str == "mat4x2")
+ return {Token::Type::kMat4x2, source, "mat4x2"};
+ if (str == "mat4x3")
+ return {Token::Type::kMat4x3, source, "mat4x3"};
+ if (str == "mat4x4")
+ return {Token::Type::kMat4x4, source, "mat4x4"};
+ if (str == "nop")
+ return {Token::Type::kNop, source, "nop"};
+ if (str == "num_workgroups")
+ return {Token::Type::kNumWorkgroups, source, "num_workgroups"};
+ if (str == "offset")
+ return {Token::Type::kOffset, source, "offset"};
+ if (str == "out")
+ return {Token::Type::kOut, source, "out"};
+ if (str == "outer_product")
+ return {Token::Type::kOuterProduct, source, "outer_product"};
+ if (str == "position")
+ return {Token::Type::kPosition, source, "position"};
+ if (str == "premerge")
+ return {Token::Type::kPremerge, source, "premerge"};
+ if (str == "private")
+ return {Token::Type::kPrivate, source, "private"};
+ if (str == "ptr")
+ return {Token::Type::kPtr, source, "ptr"};
+ if (str == "push_constant")
+ return {Token::Type::kPushConstant, source, "push_constant"};
+ if (str == "regardless")
+ return {Token::Type::kRegardless, source, "regardless"};
+ if (str == "return")
+ return {Token::Type::kReturn, source, "return"};
+ if (str == "set")
+ return {Token::Type::kSet, source, "set"};
+ if (str == "storage_buffer")
+ return {Token::Type::kStorageBuffer, source, "storage_buffer"};
+ if (str == "struct")
+ return {Token::Type::kStruct, source, "struct"};
+ if (str == "switch")
+ return {Token::Type::kSwitch, source, "switch"};
+ if (str == "true")
+ return {Token::Type::kTrue, source, "true"};
+ if (str == "type")
+ return {Token::Type::kType, source, "type"};
+ if (str == "u32")
+ return {Token::Type::kU32, source, "u32"};
+ if (str == "uniform")
+ return {Token::Type::kUniform, source, "uniform"};
+ if (str == "uniform_constant")
+ return {Token::Type::kUniformConstant, source, "uniform_constant"};
+ if (str == "unless")
+ return {Token::Type::kUnless, source, "unless"};
+ if (str == "var")
+ return {Token::Type::kVar, source, "var"};
+ if (str == "vec2")
+ return {Token::Type::kVec2, source, "vec2"};
+ if (str == "vec3")
+ return {Token::Type::kVec3, source, "vec3"};
+ if (str == "vec4")
+ return {Token::Type::kVec4, source, "vec4"};
+ if (str == "vertex")
+ return {Token::Type::kVertex, source, "vertex"};
+ if (str == "vertex_idx")
+ return {Token::Type::kVertexIdx, source, "vertex_idx"};
+ if (str == "void")
+ return {Token::Type::kVoid, source, "void"};
+ if (str == "workgroup")
+ return {Token::Type::kWorkgroup, source, "workgroup"};
+ if (str == "workgroup_size")
+ return {Token::Type::kWorkgroupSize, source, "workgroup_size"};
+
+ return {};
+}
+
+Token Lexer::check_reserved(const Source& source, const std::string& str) {
+ if (str == "asm")
+ return {Token::Type::kReservedKeyword, source, "asm"};
+ if (str == "bf16")
+ return {Token::Type::kReservedKeyword, source, "bf16"};
+ if (str == "do")
+ return {Token::Type::kReservedKeyword, source, "do"};
+ if (str == "enum")
+ return {Token::Type::kReservedKeyword, source, "enum"};
+ if (str == "f16")
+ return {Token::Type::kReservedKeyword, source, "f16"};
+ if (str == "f64")
+ return {Token::Type::kReservedKeyword, source, "f64"};
+ if (str == "for")
+ return {Token::Type::kReservedKeyword, source, "for"};
+ if (str == "i8")
+ return {Token::Type::kReservedKeyword, source, "i8"};
+ if (str == "i16")
+ return {Token::Type::kReservedKeyword, source, "i16"};
+ if (str == "i64")
+ return {Token::Type::kReservedKeyword, source, "i64"};
+ if (str == "let")
+ return {Token::Type::kReservedKeyword, source, "let"};
+ if (str == "typedef")
+ return {Token::Type::kReservedKeyword, source, "typedef"};
+ if (str == "u8")
+ return {Token::Type::kReservedKeyword, source, "u8"};
+ if (str == "u16")
+ return {Token::Type::kReservedKeyword, source, "u16"};
+ if (str == "u64")
+ return {Token::Type::kReservedKeyword, source, "u64"};
+
+ return {};
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/lexer.h b/src/reader/wgsl/lexer.h
new file mode 100644
index 0000000..0a38b50
--- /dev/null
+++ b/src/reader/wgsl/lexer.h
@@ -0,0 +1,81 @@
+// Copyright 2020 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_READER_WGSL_LEXER_H_
+#define SRC_READER_WGSL_LEXER_H_
+
+#include <string>
+
+#include "src/reader/wgsl/token.h"
+#include "src/source.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+/// Converts the input stream into a series of Tokens
+class Lexer {
+ public:
+ /// Creates a new Lexer
+ /// @param input the input to parse
+ explicit Lexer(const std::string& input);
+ ~Lexer();
+
+ /// Returns the next token in the input stream
+ /// @return Token
+ Token next();
+
+ private:
+ void skip_whitespace();
+ void skip_comments();
+
+ Token build_token_from_int_if_possible(const Source& source,
+ size_t start,
+ size_t end,
+ int32_t base);
+ Token check_keyword(const Source&, const std::string&);
+ Token check_reserved(const Source&, const std::string&);
+ Token try_float();
+ Token try_hex_integer();
+ Token try_ident();
+ Token try_integer();
+ Token try_punctuation();
+ Token try_string();
+
+ Source make_source() const;
+
+ bool is_eof() const;
+ bool is_alpha(char ch) const;
+ bool is_digit(char ch) const;
+ bool is_hex(char ch) const;
+ bool is_alphanum(char ch) const;
+ bool matches(size_t pos, const std::string& substr);
+
+ /// The source to parse
+ std::string input_;
+ /// The length of the input
+ uint32_t len_ = 0;
+ /// The current position within the input
+ uint32_t pos_ = 0;
+ /// The current line within the input
+ uint32_t line_ = 1;
+ /// The current column within the input
+ uint32_t column_ = 1;
+};
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
+
+#endif // SRC_READER_WGSL_LEXER_H_
diff --git a/src/reader/wgsl/lexer_test.cc b/src/reader/wgsl/lexer_test.cc
new file mode 100644
index 0000000..17210ff
--- /dev/null
+++ b/src/reader/wgsl/lexer_test.cc
@@ -0,0 +1,531 @@
+// Copyright 2020 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/reader/wgsl/lexer.h"
+
+#include <limits>
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using LexerTest = testing::Test;
+
+TEST_F(LexerTest, Empty) {
+ Lexer l("");
+ auto t = l.next();
+ EXPECT_TRUE(t.IsEof());
+}
+
+TEST_F(LexerTest, Skips_Whitespace) {
+ Lexer l("\t\r\n\t ident\t\n\t \r ");
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.line(), 2);
+ EXPECT_EQ(t.column(), 6);
+ EXPECT_EQ(t.to_str(), "ident");
+
+ t = l.next();
+ EXPECT_TRUE(t.IsEof());
+}
+
+TEST_F(LexerTest, Skips_Comments) {
+ Lexer l(R"(#starts with comment
+ident1 #ends with comment
+# blank line
+ ident2)");
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.line(), 2);
+ EXPECT_EQ(t.column(), 1);
+ EXPECT_EQ(t.to_str(), "ident1");
+
+ t = l.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.line(), 4);
+ EXPECT_EQ(t.column(), 2);
+ EXPECT_EQ(t.to_str(), "ident2");
+
+ t = l.next();
+ EXPECT_TRUE(t.IsEof());
+}
+
+TEST_F(LexerTest, StringTest_Parse) {
+ Lexer l(R"(id "this is string content" id2)");
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "id");
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(1, t.column());
+
+ t = l.next();
+ EXPECT_TRUE(t.IsStringLiteral());
+ EXPECT_EQ(t.to_str(), "this is string content");
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(4, t.column());
+
+ t = l.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "id2");
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(29, t.column());
+}
+
+TEST_F(LexerTest, StringTest_Unterminated) {
+ Lexer l(R"(id "this is string content)");
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "id");
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(1, t.column());
+
+ t = l.next();
+ EXPECT_TRUE(t.IsStringLiteral());
+ EXPECT_EQ(t.to_str(), "this is string content");
+
+ t = l.next();
+ EXPECT_TRUE(t.IsEof());
+}
+
+struct FloatData {
+ const char* input;
+ float result;
+};
+inline std::ostream& operator<<(std::ostream& out, FloatData data) {
+ out << std::string(data.input);
+ return out;
+}
+using FloatTest = testing::TestWithParam<FloatData>;
+TEST_P(FloatTest, Parse) {
+ auto params = GetParam();
+ Lexer l(std::string(params.input));
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsFloatLiteral());
+ EXPECT_EQ(t.to_f32(), params.result);
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(1, t.column());
+
+ t = l.next();
+ EXPECT_TRUE(t.IsEof());
+}
+INSTANTIATE_TEST_SUITE_P(LexerTest,
+ FloatTest,
+ testing::Values(FloatData{"0.0", 0.0f},
+ FloatData{"0.", 0.0f},
+ FloatData{".0", 0.0f},
+ FloatData{"5.7", 5.7f},
+ FloatData{"5.", 5.f},
+ FloatData{".7", .7f},
+ FloatData{"-0.0", 0.0f},
+ FloatData{"-.0", 0.0f},
+ FloatData{"-0.", 0.0f},
+ FloatData{"-5.7", -5.7f},
+ FloatData{"-5.", -5.f},
+ FloatData{"-.7", -.7f},
+ FloatData{"0.2e+12", 0.2e12f},
+ FloatData{"1.2e-5", 1.2e-5f},
+ FloatData{"2.57e23", 2.57e23f},
+ FloatData{"2.5e+0", 2.5f},
+ FloatData{"2.5e-0", 2.5f}));
+
+using FloatTest_Invalid = testing::TestWithParam<const char*>;
+TEST_P(FloatTest_Invalid, Handles) {
+ Lexer l(GetParam());
+
+ auto t = l.next();
+ EXPECT_FALSE(t.IsFloatLiteral());
+}
+INSTANTIATE_TEST_SUITE_P(LexerTest,
+ FloatTest_Invalid,
+ testing::Values(".",
+ "-.",
+ "2.5e+256",
+ "-2.5e+127",
+ "2.5e-300",
+ "2.5e 12",
+ "2.5e+ 123"));
+
+using IdentifierTest = testing::TestWithParam<const char*>;
+TEST_P(IdentifierTest, Parse) {
+ Lexer l(GetParam());
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.line(), 1);
+ EXPECT_EQ(t.column(), 1);
+ EXPECT_EQ(t.to_str(), GetParam());
+}
+INSTANTIATE_TEST_SUITE_P(
+ LexerTest,
+ IdentifierTest,
+ testing::Values("test01", "_test_", "test_", "_test", "_01", "_test01"));
+
+TEST_F(LexerTest, IdentifierTest_DoesNotStartWithNumber) {
+ Lexer l("01test");
+
+ auto t = l.next();
+ EXPECT_FALSE(t.IsIdentifier());
+}
+
+struct HexSignedIntData {
+ const char* input;
+ int32_t result;
+};
+inline std::ostream& operator<<(std::ostream& out, HexSignedIntData data) {
+ out << std::string(data.input);
+ return out;
+}
+
+using IntegerTest_HexSigned = testing::TestWithParam<HexSignedIntData>;
+TEST_P(IntegerTest_HexSigned, Matches) {
+ auto params = GetParam();
+ Lexer l(std::string(params.input));
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsIntLiteral());
+ EXPECT_EQ(t.line(), 1);
+ EXPECT_EQ(t.column(), 1);
+ EXPECT_EQ(t.to_i32(), params.result);
+}
+INSTANTIATE_TEST_SUITE_P(
+ LexerTest,
+ IntegerTest_HexSigned,
+ testing::Values(
+ HexSignedIntData{"0x0", 0},
+ HexSignedIntData{"0x42", 66},
+ HexSignedIntData{"-0x42", -66},
+ HexSignedIntData{"0xeF1Abc9", 250719177},
+ HexSignedIntData{"-0x80000000", std::numeric_limits<int32_t>::min()},
+ HexSignedIntData{"0x7FFFFFFF", std::numeric_limits<int32_t>::max()}));
+
+TEST_F(LexerTest, IntegerTest_HexSignedTooLarge) {
+ Lexer l("0x80000000");
+ auto t = l.next();
+ ASSERT_TRUE(t.IsError());
+ EXPECT_EQ(t.to_str(), "i32 (0x80000000) too large");
+}
+
+TEST_F(LexerTest, IntegerTest_HexSignedTooSmall) {
+ Lexer l("-0x8000000F");
+ auto t = l.next();
+ ASSERT_TRUE(t.IsError());
+ EXPECT_EQ(t.to_str(), "i32 (-0x8000000F) too small");
+}
+
+struct HexUnsignedIntData {
+ const char* input;
+ uint32_t result;
+};
+inline std::ostream& operator<<(std::ostream& out, HexUnsignedIntData data) {
+ out << std::string(data.input);
+ return out;
+}
+using IntegerTest_HexUnsigned = testing::TestWithParam<HexUnsignedIntData>;
+TEST_P(IntegerTest_HexUnsigned, Matches) {
+ auto params = GetParam();
+ Lexer l(std::string(params.input));
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsUintLiteral());
+ EXPECT_EQ(t.line(), 1);
+ EXPECT_EQ(t.column(), 1);
+ EXPECT_EQ(t.to_u32(), params.result);
+}
+INSTANTIATE_TEST_SUITE_P(
+ LexerTest,
+ IntegerTest_HexUnsigned,
+ testing::Values(HexUnsignedIntData{"0x0u", 0},
+ HexUnsignedIntData{"0x42u", 66},
+ HexUnsignedIntData{"0xeF1Abc9u", 250719177},
+ HexUnsignedIntData{"0x0u",
+ std::numeric_limits<uint32_t>::min()},
+ HexUnsignedIntData{"0xFFFFFFFFu",
+ std::numeric_limits<uint32_t>::max()}));
+
+TEST_F(LexerTest, IntegerTest_HexUnsignedTooLarge) {
+ Lexer l("0xffffffffffu");
+ auto t = l.next();
+ ASSERT_TRUE(t.IsError());
+ EXPECT_EQ(t.to_str(), "u32 (0xffffffffff) too large");
+}
+
+struct UnsignedIntData {
+ const char* input;
+ uint32_t result;
+};
+inline std::ostream& operator<<(std::ostream& out, UnsignedIntData data) {
+ out << std::string(data.input);
+ return out;
+}
+using IntegerTest_Unsigned = testing::TestWithParam<UnsignedIntData>;
+TEST_P(IntegerTest_Unsigned, Matches) {
+ auto params = GetParam();
+ Lexer l(params.input);
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsUintLiteral());
+ EXPECT_EQ(t.to_u32(), params.result);
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(1, t.column());
+}
+INSTANTIATE_TEST_SUITE_P(LexerTest,
+ IntegerTest_Unsigned,
+ testing::Values(UnsignedIntData{"0u", 0u},
+ UnsignedIntData{"123u", 123u},
+ UnsignedIntData{"4294967295u",
+ 4294967295u}));
+
+struct SignedIntData {
+ const char* input;
+ int32_t result;
+};
+inline std::ostream& operator<<(std::ostream& out, SignedIntData data) {
+ out << std::string(data.input);
+ return out;
+}
+using IntegerTest_Signed = testing::TestWithParam<SignedIntData>;
+TEST_P(IntegerTest_Signed, Matches) {
+ auto params = GetParam();
+ Lexer l(params.input);
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsIntLiteral());
+ EXPECT_EQ(t.to_i32(), params.result);
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(1, t.column());
+}
+INSTANTIATE_TEST_SUITE_P(
+ LexerTest,
+ IntegerTest_Signed,
+ testing::Values(SignedIntData{"0", 0},
+ SignedIntData{"-2", -2},
+ SignedIntData{"2", 2},
+ SignedIntData{"123", 123},
+ SignedIntData{"2147483647", 2147483647},
+ SignedIntData{"-2147483648", -2147483648}));
+
+using IntegerTest_Invalid = testing::TestWithParam<const char*>;
+TEST_P(IntegerTest_Invalid, Parses) {
+ Lexer l(GetParam());
+
+ auto t = l.next();
+ EXPECT_FALSE(t.IsIntLiteral());
+ EXPECT_FALSE(t.IsUintLiteral());
+}
+INSTANTIATE_TEST_SUITE_P(LexerTest,
+ IntegerTest_Invalid,
+ testing::Values("2147483648", "4294967296u"));
+
+struct TokenData {
+ const char* input;
+ Token::Type type;
+};
+inline std::ostream& operator<<(std::ostream& out, TokenData data) {
+ out << std::string(data.input);
+ return out;
+}
+using PunctuationTest = testing::TestWithParam<TokenData>;
+TEST_P(PunctuationTest, Parses) {
+ auto params = GetParam();
+ Lexer l(params.input);
+
+ auto t = l.next();
+ EXPECT_TRUE(t.Is(params.type));
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(1, t.column());
+
+ t = l.next();
+ EXPECT_EQ(1 + std::string(params.input).size(), t.column());
+}
+INSTANTIATE_TEST_SUITE_P(
+ LexerTest,
+ PunctuationTest,
+ testing::Values(TokenData{"&", Token::Type::kAnd},
+ TokenData{"&&", Token::Type::kAndAnd},
+ TokenData{"->", Token::Type::kArrow},
+ TokenData{"[[", Token::Type::kAttrLeft},
+ TokenData{"]]", Token::Type::kAttrRight},
+ TokenData{"/", Token::Type::kForwardSlash},
+ TokenData{"!", Token::Type::kBang},
+ TokenData{"[", Token::Type::kBraceLeft},
+ TokenData{"]", Token::Type::kBraceRight},
+ TokenData{"{", Token::Type::kBracketLeft},
+ TokenData{"}", Token::Type::kBracketRight},
+ TokenData{":", Token::Type::kColon},
+ TokenData{",", Token::Type::kComma},
+ TokenData{"=", Token::Type::kEqual},
+ TokenData{"==", Token::Type::kEqualEqual},
+ TokenData{">", Token::Type::kGreaterThan},
+ TokenData{">=", Token::Type::kGreaterThanEqual},
+ TokenData{"<", Token::Type::kLessThan},
+ TokenData{"<=", Token::Type::kLessThanEqual},
+ TokenData{"%", Token::Type::kMod},
+ TokenData{"!=", Token::Type::kNotEqual},
+ TokenData{"-", Token::Type::kMinus},
+ TokenData{"::", Token::Type::kNamespace},
+ TokenData{".", Token::Type::kPeriod},
+ TokenData{"+", Token::Type::kPlus},
+ TokenData{"|", Token::Type::kOr},
+ TokenData{"||", Token::Type::kOrOr},
+ TokenData{"(", Token::Type::kParenLeft},
+ TokenData{")", Token::Type::kParenRight},
+ TokenData{";", Token::Type::kSemicolon},
+ TokenData{"*", Token::Type::kStar},
+ TokenData{"^", Token::Type::kXor}));
+
+using KeywordTest = testing::TestWithParam<TokenData>;
+TEST_P(KeywordTest, Parses) {
+ auto params = GetParam();
+ Lexer l(params.input);
+
+ auto t = l.next();
+ EXPECT_TRUE(t.Is(params.type));
+ EXPECT_EQ(1, t.line());
+ EXPECT_EQ(1, t.column());
+
+ t = l.next();
+ EXPECT_EQ(1 + std::string(params.input).size(), t.column());
+}
+INSTANTIATE_TEST_SUITE_P(
+ LexerTest,
+ KeywordTest,
+ testing::Values(
+ TokenData{"all", Token::Type::kAll},
+ TokenData{"any", Token::Type::kAny},
+ TokenData{"array", Token::Type::kArray},
+ TokenData{"as", Token::Type::kAs},
+ TokenData{"binding", Token::Type::kBinding},
+ TokenData{"block", Token::Type::kBlock},
+ TokenData{"bool", Token::Type::kBool},
+ TokenData{"break", Token::Type::kBreak},
+ TokenData{"builtin", Token::Type::kBuiltin},
+ TokenData{"case", Token::Type::kCase},
+ TokenData{"cast", Token::Type::kCast},
+ TokenData{"compute", Token::Type::kCompute},
+ TokenData{"const", Token::Type::kConst},
+ TokenData{"continue", Token::Type::kContinue},
+ TokenData{"continuing", Token::Type::kContinuing},
+ TokenData{"coarse", Token::Type::kCoarse},
+ TokenData{"default", Token::Type::kDefault},
+ TokenData{"dot", Token::Type::kDot},
+ TokenData{"dpdx", Token::Type::kDpdx},
+ TokenData{"dpdy", Token::Type::kDpdy},
+ TokenData{"else", Token::Type::kElse},
+ TokenData{"elseif", Token::Type::kElseIf},
+ TokenData{"entry_point", Token::Type::kEntryPoint},
+ TokenData{"f32", Token::Type::kF32},
+ TokenData{"fallthrough", Token::Type::kFallthrough},
+ TokenData{"false", Token::Type::kFalse},
+ TokenData{"fine", Token::Type::kFine},
+ TokenData{"fn", Token::Type::kFn},
+ TokenData{"frag_coord", Token::Type::kFragCoord},
+ TokenData{"frag_depth", Token::Type::kFragDepth},
+ TokenData{"fragment", Token::Type::kFragment},
+ TokenData{"front_facing", Token::Type::kFrontFacing},
+ TokenData{"function", Token::Type::kFunction},
+ TokenData{"fwidth", Token::Type::kFwidth},
+ TokenData{"global_invocation_id", Token::Type::kGlobalInvocationId},
+ TokenData{"i32", Token::Type::kI32},
+ TokenData{"if", Token::Type::kIf},
+ TokenData{"image", Token::Type::kImage},
+ TokenData{"import", Token::Type::kImport},
+ TokenData{"in", Token::Type::kIn},
+ TokenData{"instance_idx", Token::Type::kInstanceIdx},
+ TokenData{"is_nan", Token::Type::kIsNan},
+ TokenData{"is_inf", Token::Type::kIsInf},
+ TokenData{"is_finite", Token::Type::kIsFinite},
+ TokenData{"is_normal", Token::Type::kIsNormal},
+ TokenData{"kill", Token::Type::kKill},
+ TokenData{"local_invocation_id", Token::Type::kLocalInvocationId},
+ TokenData{"local_invocation_idx", Token::Type::kLocalInvocationIdx},
+ TokenData{"location", Token::Type::kLocation},
+ TokenData{"loop", Token::Type::kLoop},
+ TokenData{"mat2x2", Token::Type::kMat2x2},
+ TokenData{"mat2x3", Token::Type::kMat2x3},
+ TokenData{"mat2x4", Token::Type::kMat2x4},
+ TokenData{"mat3x2", Token::Type::kMat3x2},
+ TokenData{"mat3x3", Token::Type::kMat3x3},
+ TokenData{"mat3x4", Token::Type::kMat3x4},
+ TokenData{"mat4x2", Token::Type::kMat4x2},
+ TokenData{"mat4x3", Token::Type::kMat4x3},
+ TokenData{"mat4x4", Token::Type::kMat4x4},
+ TokenData{"nop", Token::Type::kNop},
+ TokenData{"num_workgroups", Token::Type::kNumWorkgroups},
+ TokenData{"offset", Token::Type::kOffset},
+ TokenData{"out", Token::Type::kOut},
+ TokenData{"outer_product", Token::Type::kOuterProduct},
+ TokenData{"position", Token::Type::kPosition},
+ TokenData{"premerge", Token::Type::kPremerge},
+ TokenData{"private", Token::Type::kPrivate},
+ TokenData{"ptr", Token::Type::kPtr},
+ TokenData{"push_constant", Token::Type::kPushConstant},
+ TokenData{"regardless", Token::Type::kRegardless},
+ TokenData{"return", Token::Type::kReturn},
+ TokenData{"set", Token::Type::kSet},
+ TokenData{"storage_buffer", Token::Type::kStorageBuffer},
+ TokenData{"struct", Token::Type::kStruct},
+ TokenData{"switch", Token::Type::kSwitch},
+ TokenData{"true", Token::Type::kTrue},
+ TokenData{"type", Token::Type::kType},
+ TokenData{"u32", Token::Type::kU32},
+ TokenData{"uniform", Token::Type::kUniform},
+ TokenData{"uniform_constant", Token::Type::kUniformConstant},
+ TokenData{"unless", Token::Type::kUnless},
+ TokenData{"var", Token::Type::kVar},
+ TokenData{"vec2", Token::Type::kVec2},
+ TokenData{"vec3", Token::Type::kVec3},
+ TokenData{"vec4", Token::Type::kVec4},
+ TokenData{"vertex", Token::Type::kVertex},
+ TokenData{"vertex_idx", Token::Type::kVertexIdx},
+ TokenData{"void", Token::Type::kVoid},
+ TokenData{"workgroup", Token::Type::kWorkgroup},
+ TokenData{"workgroup_size", Token::Type::kWorkgroupSize}));
+
+using KeywordTest_Reserved = testing::TestWithParam<const char*>;
+TEST_P(KeywordTest_Reserved, Parses) {
+ auto keyword = GetParam();
+ Lexer l(keyword);
+
+ auto t = l.next();
+ EXPECT_TRUE(t.IsReservedKeyword());
+ EXPECT_EQ(t.to_str(), keyword);
+}
+INSTANTIATE_TEST_SUITE_P(LexerTest,
+ KeywordTest_Reserved,
+ testing::Values("asm",
+ "bf16",
+ "do",
+ "enum",
+ "f16",
+ "f64",
+ "for",
+ "i8",
+ "i16",
+ "i64",
+ "let",
+ "typedef",
+ "u8",
+ "u16",
+ "u64"));
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser.cc b/src/reader/wgsl/parser.cc
new file mode 100644
index 0000000..f84dc62
--- /dev/null
+++ b/src/reader/wgsl/parser.cc
@@ -0,0 +1,43 @@
+// Copyright 2020 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/reader/wgsl/parser.h"
+
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+Parser::Parser(const std::string& input)
+ : Reader(), impl_(std::make_unique<ParserImpl>(input)) {}
+
+Parser::~Parser() = default;
+
+bool Parser::Parse() {
+ bool ret = impl_->Parse();
+
+ if (impl_->has_error())
+ set_error(impl_->error());
+
+ return ret;
+}
+
+ast::Module Parser::module() {
+ return impl_->module();
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser.h b/src/reader/wgsl/parser.h
new file mode 100644
index 0000000..b3b27d9
--- /dev/null
+++ b/src/reader/wgsl/parser.h
@@ -0,0 +1,52 @@
+// Copyright 2020 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_READER_WGSL_PARSER_H_
+#define SRC_READER_WGSL_PARSER_H_
+
+#include <memory>
+#include <string>
+
+#include "src/reader/reader.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+class ParserImpl;
+
+/// Parser for WGSL source data
+class Parser : public Reader {
+ public:
+ /// Creates a new parser
+ /// @param input the input string to parse
+ explicit Parser(const std::string& input);
+ ~Parser() override;
+
+ /// Run the parser
+ /// @returns true if the parse was successful, false otherwise.
+ bool Parse() override;
+
+ /// @returns the module. The module in the parser will be reset after this.
+ ast::Module module() override;
+
+ private:
+ std::unique_ptr<ParserImpl> impl_;
+};
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
+
+#endif // SRC_READER_WGSL_PARSER_H_
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
new file mode 100644
index 0000000..58d4bf2
--- /dev/null
+++ b/src/reader/wgsl/parser_impl.cc
@@ -0,0 +1,3061 @@
+// Copyright 2020 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/reader/wgsl/parser_impl.h"
+
+#include <memory>
+
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/as_expression.h"
+#include "src/ast/binding_decoration.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/break_statement.h"
+#include "src/ast/builtin_decoration.h"
+#include "src/ast/call_expression.h"
+#include "src/ast/case_statement.h"
+#include "src/ast/cast_expression.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/continue_statement.h"
+#include "src/ast/decorated_variable.h"
+#include "src/ast/else_statement.h"
+#include "src/ast/fallthrough_statement.h"
+#include "src/ast/float_literal.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/if_statement.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/kill_statement.h"
+#include "src/ast/location_decoration.h"
+#include "src/ast/member_accessor_expression.h"
+#include "src/ast/nop_statement.h"
+#include "src/ast/relational_expression.h"
+#include "src/ast/return_statement.h"
+#include "src/ast/set_decoration.h"
+#include "src/ast/statement_condition.h"
+#include "src/ast/struct_member_offset_decoration.h"
+#include "src/ast/switch_statement.h"
+#include "src/ast/type/alias_type.h"
+#include "src/ast/type/array_type.h"
+#include "src/ast/type/bool_type.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/matrix_type.h"
+#include "src/ast/type/pointer_type.h"
+#include "src/ast/type/struct_type.h"
+#include "src/ast/type/u32_type.h"
+#include "src/ast/type/vector_type.h"
+#include "src/ast/type/void_type.h"
+#include "src/ast/type_initializer_expression.h"
+#include "src/ast/uint_literal.h"
+#include "src/ast/unary_derivative.h"
+#include "src/ast/unary_derivative_expression.h"
+#include "src/ast/unary_method.h"
+#include "src/ast/unary_method_expression.h"
+#include "src/ast/unary_op.h"
+#include "src/ast/unary_op_expression.h"
+#include "src/ast/variable_statement.h"
+#include "src/reader/wgsl/lexer.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+ParserImpl::ParserImpl(const std::string& input)
+ : lexer_(std::make_unique<Lexer>(input)) {}
+
+ParserImpl::~ParserImpl() = default;
+
+void ParserImpl::set_error(const Token& t, const std::string& err) {
+ auto prefix =
+ std::to_string(t.line()) + ":" + std::to_string(t.column()) + ": ";
+
+ if (t.IsReservedKeyword()) {
+ error_ = prefix + "reserved token (" + t.to_str() + ") found";
+ return;
+ }
+ if (t.IsError()) {
+ error_ = prefix + t.to_str();
+ return;
+ }
+
+ if (err.size() != 0) {
+ error_ = prefix + err;
+ } else {
+ error_ = prefix + "invalid token (" + t.to_name() + ") encountered";
+ }
+}
+
+void ParserImpl::set_error(const Token& t) {
+ set_error(t, "");
+}
+
+Token ParserImpl::next() {
+ if (!token_queue_.empty()) {
+ auto t = token_queue_.front();
+ token_queue_.pop_front();
+ return t;
+ }
+ return lexer_->next();
+}
+
+Token ParserImpl::peek(size_t idx) {
+ while (token_queue_.size() < (idx + 1))
+ token_queue_.push_back(lexer_->next());
+
+ return token_queue_[idx];
+}
+
+Token ParserImpl::peek() {
+ return peek(0);
+}
+
+void ParserImpl::register_alias(const std::string& name,
+ ast::type::Type* type) {
+ assert(type);
+ registered_aliases_[name] = type;
+}
+
+ast::type::Type* ParserImpl::get_alias(const std::string& name) {
+ if (registered_aliases_.find(name) == registered_aliases_.end()) {
+ return nullptr;
+ }
+ return registered_aliases_[name];
+}
+
+bool ParserImpl::Parse() {
+ translation_unit();
+ return !has_error();
+}
+
+// translation_unit
+// : global_decl* EOF
+void ParserImpl::translation_unit() {
+ for (;;) {
+ global_decl();
+ if (has_error())
+ return;
+
+ if (peek().IsEof())
+ break;
+ }
+
+ assert(module_.IsValid());
+}
+
+// global_decl
+// : SEMICOLON
+// | import_decl SEMICOLON
+// | global_variable_decl SEMICLON
+// | global_constant_decl SEMICOLON
+// | entry_point_decl SEMICOLON
+// | type_alias SEMICOLON
+// | function_decl
+void ParserImpl::global_decl() {
+ auto t = peek();
+ if (t.IsEof())
+ return;
+
+ if (t.IsSemicolon()) {
+ next(); // consume the peek
+ return;
+ }
+
+ auto import = import_decl();
+ if (has_error())
+ return;
+ if (import != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ';' for import");
+ return;
+ }
+ module_.AddImport(std::move(import));
+ return;
+ }
+
+ auto gv = global_variable_decl();
+ if (has_error())
+ return;
+ if (gv != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ';' for variable declaration");
+ return;
+ }
+ module_.AddGlobalVariable(std::move(gv));
+ return;
+ }
+
+ auto gc = global_constant_decl();
+ if (has_error())
+ return;
+ if (gc != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ';' for constant declaration");
+ return;
+ }
+ module_.AddGlobalVariable(std::move(gc));
+ return;
+ }
+
+ auto ep = entry_point_decl();
+ if (has_error())
+ return;
+ if (ep != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ';' for entry point");
+ return;
+ }
+ module_.AddEntryPoint(std::move(ep));
+ return;
+ }
+
+ auto ta = type_alias();
+ if (has_error())
+ return;
+ if (ta != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ';' for type alias");
+ return;
+ }
+ module_.AddAliasType(ta);
+ return;
+ }
+
+ auto func = function_decl();
+ if (has_error())
+ return;
+ if (func != nullptr) {
+ module_.AddFunction(std::move(func));
+ return;
+ }
+
+ set_error(t);
+}
+
+// import_decl
+// : IMPORT STRING_LITERAL AS (IDENT NAMESPACE)* IDENT
+std::unique_ptr<ast::Import> ParserImpl::import_decl() {
+ auto t = peek();
+ if (!t.IsImport())
+ return {};
+
+ auto source = t.source();
+ next(); // consume the import token
+
+ t = next();
+ if (!t.IsStringLiteral()) {
+ set_error(t, "missing path for import");
+ return {};
+ }
+ auto path = t.to_str();
+ if (path.length() == 0) {
+ set_error(t, "import path must not be empty");
+ return {};
+ }
+
+ t = next();
+ if (!t.IsAs()) {
+ set_error(t, "missing 'as' for import");
+ return {};
+ }
+
+ std::string name = "";
+ for (;;) {
+ t = peek();
+ if (!t.IsIdentifier()) {
+ break;
+ }
+ next(); // consume the peek
+
+ name += t.to_str();
+
+ t = peek();
+ if (!t.IsNamespace()) {
+ break;
+ }
+ next(); // consume the peek
+
+ name += "::";
+ }
+ if (name.length() == 0) {
+ if (t.IsEof() || t.IsSemicolon()) {
+ set_error(t, "missing name for import");
+ } else {
+ set_error(t, "invalid name for import");
+ }
+ return {};
+ }
+ if (name.length() > 2) {
+ auto end = name.length() - 1;
+ if (name[end] == ':' && name[end - 1] == ':') {
+ set_error(t, "invalid name for import");
+ return {};
+ }
+ }
+ return std::make_unique<ast::Import>(source, path, name);
+}
+
+// global_variable_decl
+// : variable_decoration_list variable_decl
+// | variable_decoration_list variable_decl EQUAL const_expr
+std::unique_ptr<ast::Variable> ParserImpl::global_variable_decl() {
+ auto decos = variable_decoration_list();
+ if (has_error())
+ return nullptr;
+
+ auto var = variable_decl();
+ if (has_error())
+ return nullptr;
+ if (var == nullptr) {
+ if (decos.size() > 0)
+ set_error(peek(), "error parsing variable declaration");
+
+ return nullptr;
+ }
+
+ if (decos.size() > 0) {
+ auto dv = std::make_unique<ast::DecoratedVariable>();
+ dv->set_source(var->source());
+ dv->set_name(var->name());
+ dv->set_type(var->type());
+ dv->set_storage_class(var->storage_class());
+ dv->set_decorations(std::move(decos));
+
+ var = std::move(dv);
+ }
+
+ auto t = peek();
+ if (t.IsEqual()) {
+ next(); // Consume the peek
+
+ auto expr = const_expr();
+ if (has_error())
+ return nullptr;
+ if (expr == nullptr) {
+ set_error(peek(), "invalid expression");
+ return nullptr;
+ }
+
+ var->set_initializer(std::move(expr));
+ }
+ return var;
+}
+
+// global_constant_decl
+// : CONST variable_ident_decl EQUAL const_expr
+std::unique_ptr<ast::Variable> ParserImpl::global_constant_decl() {
+ auto t = peek();
+ if (!t.IsConst())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ std::string name;
+ ast::type::Type* type;
+ std::tie(name, type) = variable_ident_decl();
+ if (has_error())
+ return nullptr;
+ if (name == "" || type == nullptr) {
+ set_error(peek(), "error parsing constant variable identifier");
+ return nullptr;
+ }
+
+ auto var = std::make_unique<ast::Variable>(source, name,
+ ast::StorageClass::kNone, type);
+ var->set_is_const(true);
+
+ t = next();
+ if (!t.IsEqual()) {
+ set_error(t, "missing = for const declaration");
+ return nullptr;
+ }
+
+ auto init = const_expr();
+ if (has_error())
+ return nullptr;
+ if (init == nullptr) {
+ set_error(peek(), "error parsing constant initializer");
+ return nullptr;
+ }
+ var->set_initializer(std::move(init));
+
+ return var;
+}
+
+// variable_decoration_list
+// : ATTR_LEFT variable_decoration (COMMA variable_decoration)* ATTR_RIGHT
+std::vector<std::unique_ptr<ast::VariableDecoration>>
+ParserImpl::variable_decoration_list() {
+ std::vector<std::unique_ptr<ast::VariableDecoration>> decos;
+
+ auto t = peek();
+ if (!t.IsAttrLeft())
+ return decos;
+
+ next(); // consume the peek
+
+ auto deco = variable_decoration();
+ if (has_error())
+ return {};
+ if (deco == nullptr) {
+ t = peek();
+ if (t.IsAttrRight()) {
+ set_error(t, "empty variable decoration list");
+ return {};
+ }
+ set_error(t, "missing variable decoration for decoration list");
+ return {};
+ }
+ for (;;) {
+ decos.push_back(std::move(deco));
+
+ t = peek();
+ if (!t.IsComma()) {
+ break;
+ }
+ next(); // consume the peek
+
+ deco = variable_decoration();
+ if (has_error())
+ return {};
+ if (deco == nullptr) {
+ set_error(peek(), "missing variable decoration after comma");
+ return {};
+ }
+ }
+
+ t = peek();
+ if (!t.IsAttrRight()) {
+ deco = variable_decoration();
+ if (deco != nullptr) {
+ set_error(t, "missing comma in variable decoration list");
+ return {};
+ }
+ set_error(t, "missing ]] for variable decoration");
+ return {};
+ }
+ next(); // consume the peek
+
+ return decos;
+}
+
+// variable_decoration
+// : LOCATION INT_LITERAL
+// | BUILTIN builtin_decoration
+// | BINDING INT_LITERAL
+// | SET INT_LITERAL
+std::unique_ptr<ast::VariableDecoration> ParserImpl::variable_decoration() {
+ auto t = peek();
+ if (t.IsLocation()) {
+ next(); // consume the peek
+
+ t = next();
+ if (!t.IsIntLiteral()) {
+ set_error(t, "invalid value for location decoration");
+ return {};
+ }
+
+ return std::make_unique<ast::LocationDecoration>(t.to_i32());
+ }
+ if (t.IsBuiltin()) {
+ next(); // consume the peek
+
+ ast::Builtin builtin = builtin_decoration();
+ if (has_error())
+ return {};
+ if (builtin == ast::Builtin::kNone) {
+ set_error(peek(), "invalid value for builtin decoration");
+ return {};
+ }
+
+ return std::make_unique<ast::BuiltinDecoration>(builtin);
+ }
+ if (t.IsBinding()) {
+ next(); // consume the peek
+
+ t = next();
+ if (!t.IsIntLiteral()) {
+ set_error(t, "invalid value for binding decoration");
+ return {};
+ }
+
+ return std::make_unique<ast::BindingDecoration>(t.to_i32());
+ }
+ if (t.IsSet()) {
+ next(); // consume the peek
+
+ t = next();
+ if (!t.IsIntLiteral()) {
+ set_error(t, "invalid value for set decoration");
+ return {};
+ }
+
+ return std::make_unique<ast::SetDecoration>(t.to_i32());
+ }
+
+ return nullptr;
+}
+
+// builtin_decoration
+// : POSITION
+// | VERTEX_IDX
+// | INSTANCE_IDX
+// | FRONT_FACING
+// | FRAG_COORD
+// | FRAG_DEPTH
+// | NUM_WORKGROUPS
+// | WORKGROUP_SIZE
+// | LOCAL_INVOC_ID
+// | LOCAL_INVOC_IDX
+// | GLOBAL_INVOC_ID
+ast::Builtin ParserImpl::builtin_decoration() {
+ auto t = peek();
+ if (t.IsPosition()) {
+ next(); // consume the peek
+ return ast::Builtin::kPosition;
+ }
+ if (t.IsVertexIdx()) {
+ next(); // consume the peek
+ return ast::Builtin::kVertexIdx;
+ }
+ if (t.IsInstanceIdx()) {
+ next(); // consume the peek
+ return ast::Builtin::kInstanceIdx;
+ }
+ if (t.IsFrontFacing()) {
+ next(); // consume the peek
+ return ast::Builtin::kFrontFacing;
+ }
+ if (t.IsFragCoord()) {
+ next(); // consume the peek
+ return ast::Builtin::kFragCoord;
+ }
+ if (t.IsFragDepth()) {
+ next(); // consume the peek
+ return ast::Builtin::kFragDepth;
+ }
+ if (t.IsNumWorkgroups()) {
+ next(); // consume the peek
+ return ast::Builtin::kNumWorkgroups;
+ }
+ if (t.IsWorkgroupSize()) {
+ next(); // consume the peek
+ return ast::Builtin::kWorkgroupSize;
+ }
+ if (t.IsLocalInvocationId()) {
+ next(); // consume the peek
+ return ast::Builtin::kLocalInvocationId;
+ }
+ if (t.IsLocalInvocationIdx()) {
+ next(); // consume the peek
+ return ast::Builtin::kLocalInvocationIdx;
+ }
+ if (t.IsGlobalInvocationId()) {
+ next(); // consume the peek
+ return ast::Builtin::kGlobalInvocationId;
+ }
+ return ast::Builtin::kNone;
+}
+
+// variable_decl
+// : VAR variable_storage_decoration? variable_ident_decl
+std::unique_ptr<ast::Variable> ParserImpl::variable_decl() {
+ auto t = peek();
+ auto source = t.source();
+ if (!t.IsVar())
+ return nullptr;
+
+ next(); // Consume the peek
+
+ auto sc = variable_storage_decoration();
+ if (has_error())
+ return {};
+
+ std::string name;
+ ast::type::Type* type;
+ std::tie(name, type) = variable_ident_decl();
+ if (has_error())
+ return nullptr;
+ if (name == "" || type == nullptr) {
+ set_error(peek(), "invalid identifier declaration");
+ return nullptr;
+ }
+
+ return std::make_unique<ast::Variable>(source, name, sc, type);
+}
+
+// variable_ident_decl
+// : IDENT COLON type_decl
+std::pair<std::string, ast::type::Type*> ParserImpl::variable_ident_decl() {
+ auto t = peek();
+ if (!t.IsIdentifier())
+ return {};
+
+ auto name = t.to_str();
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsColon()) {
+ set_error(t, "missing : for identifier declaration");
+ return {};
+ }
+
+ auto type = type_decl();
+ if (has_error())
+ return {};
+ if (type == nullptr) {
+ set_error(peek(), "invalid type for identifier declaration");
+ return {};
+ }
+
+ return {name, type};
+}
+
+// variable_storage_decoration
+// : LESS_THAN storage_class GREATER_THAN
+ast::StorageClass ParserImpl::variable_storage_decoration() {
+ auto t = peek();
+ if (!t.IsLessThan())
+ return ast::StorageClass::kNone;
+
+ next(); // Consume the peek
+
+ auto sc = storage_class();
+ if (has_error())
+ return sc;
+ if (sc == ast::StorageClass::kNone) {
+ set_error(peek(), "invalid storage class for variable decoration");
+ return sc;
+ }
+
+ t = next();
+ if (!t.IsGreaterThan()) {
+ set_error(t, "missing > for variable decoration");
+ return ast::StorageClass::kNone;
+ }
+
+ return sc;
+}
+
+// type_alias
+// : TYPE IDENT EQUAL type_decl
+// | TYPE IDENT EQUAL struct_decl
+ast::type::AliasType* ParserImpl::type_alias() {
+ auto t = peek();
+ if (!t.IsType())
+ return nullptr;
+
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "missing identifier for type alias");
+ return nullptr;
+ }
+ auto name = t.to_str();
+
+ t = next();
+ if (!t.IsEqual()) {
+ set_error(t, "missing = for type alias");
+ return nullptr;
+ }
+
+ auto tm = TypeManager::Instance();
+
+ auto type = type_decl();
+ if (has_error())
+ return nullptr;
+ if (type == nullptr) {
+ auto str = struct_decl();
+ if (has_error())
+ return nullptr;
+ if (str == nullptr) {
+ set_error(peek(), "invalid type alias");
+ return nullptr;
+ }
+
+ str->set_name(name);
+ type = tm->Get(std::move(str));
+ }
+ if (type == nullptr) {
+ set_error(peek(), "invalid type for alias");
+ return nullptr;
+ }
+
+ auto alias = tm->Get(std::make_unique<ast::type::AliasType>(name, type));
+ register_alias(name, alias);
+
+ return alias->AsAlias();
+}
+
+// type_decl
+// : IDENTIFIER
+// | BOOL
+// | FLOAT32
+// | INT32
+// | UINT32
+// | VEC2 LESS_THAN type_decl GREATER_THAN
+// | VEC3 LESS_THAN type_decl GREATER_THAN
+// | VEC3 LESS_THAN type_decl GREATER_THAN
+// | PTR LESS_THAN storage_class, type_decl GREATER_THAN
+// | ARRAY LESS_THAN type_decl COMMA INT_LITERAL GREATER_THAN
+// | ARRAY LESS_THAN type_decl GREATER_THAN
+// | MAT2x2 LESS_THAN type_decl GREATER_THAN
+// | MAT2x3 LESS_THAN type_decl GREATER_THAN
+// | MAT2x4 LESS_THAN type_decl GREATER_THAN
+// | MAT3x2 LESS_THAN type_decl GREATER_THAN
+// | MAT3x3 LESS_THAN type_decl GREATER_THAN
+// | MAT3x4 LESS_THAN type_decl GREATER_THAN
+// | MAT4x2 LESS_THAN type_decl GREATER_THAN
+// | MAT4x3 LESS_THAN type_decl GREATER_THAN
+// | MAT4x4 LESS_THAN type_decl GREATER_THAN
+ast::type::Type* ParserImpl::type_decl() {
+ auto tm = TypeManager::Instance();
+
+ auto t = peek();
+ if (t.IsIdentifier()) {
+ next(); // Consume the peek
+ auto alias = get_alias(t.to_str());
+ if (alias == nullptr) {
+ set_error(t, "unknown type alias '" + t.to_str() + "'");
+ return nullptr;
+ }
+ return alias;
+ }
+ if (t.IsBool()) {
+ next(); // Consume the peek
+ return tm->Get(std::make_unique<ast::type::BoolType>());
+ }
+ if (t.IsF32()) {
+ next(); // Consume the peek
+ return tm->Get(std::make_unique<ast::type::F32Type>());
+ }
+ if (t.IsI32()) {
+ next(); // Consume the peek
+ return tm->Get(std::make_unique<ast::type::I32Type>());
+ }
+ if (t.IsU32()) {
+ next(); // Consume the peek
+ return tm->Get(std::make_unique<ast::type::U32Type>());
+ }
+ if (t.IsVec2() || t.IsVec3() || t.IsVec4()) {
+ return type_decl_vector(t);
+ }
+ if (t.IsPtr()) {
+ return type_decl_pointer(t);
+ }
+ if (t.IsArray()) {
+ return type_decl_array(t);
+ }
+ if (t.IsMat2x2() || t.IsMat2x3() || t.IsMat2x4() || t.IsMat3x2() ||
+ t.IsMat3x3() || t.IsMat3x4() || t.IsMat4x2() || t.IsMat4x3() ||
+ t.IsMat4x4()) {
+ return type_decl_matrix(t);
+ }
+ return nullptr;
+}
+
+ast::type::Type* ParserImpl::type_decl_pointer(Token t) {
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsLessThan()) {
+ set_error(t, "missing < for ptr declaration");
+ return nullptr;
+ }
+
+ auto sc = storage_class();
+ if (has_error())
+ return nullptr;
+ if (sc == ast::StorageClass::kNone) {
+ set_error(peek(), "missing storage class for ptr declaration");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsComma()) {
+ set_error(t, "missing , for ptr declaration");
+ return nullptr;
+ }
+
+ auto subtype = type_decl();
+ if (has_error())
+ return nullptr;
+ if (subtype == nullptr) {
+ set_error(peek(), "missing type for ptr declaration");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsGreaterThan()) {
+ set_error(t, "missing > for ptr declaration");
+ return nullptr;
+ }
+
+ return TypeManager::Instance()->Get(
+ std::make_unique<ast::type::PointerType>(subtype, sc));
+}
+
+ast::type::Type* ParserImpl::type_decl_vector(Token t) {
+ next(); // Consume the peek
+
+ size_t count = 2;
+ if (t.IsVec3())
+ count = 3;
+ else if (t.IsVec4())
+ count = 4;
+
+ t = next();
+ if (!t.IsLessThan()) {
+ set_error(t, "missing < for vector");
+ return nullptr;
+ }
+
+ auto subtype = type_decl();
+ if (has_error())
+ return nullptr;
+ if (subtype == nullptr) {
+ set_error(peek(), "unable to determine subtype for vector");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsGreaterThan()) {
+ set_error(t, "missing > for vector");
+ return nullptr;
+ }
+
+ return TypeManager::Instance()->Get(
+ std::make_unique<ast::type::VectorType>(subtype, count));
+}
+
+ast::type::Type* ParserImpl::type_decl_array(Token t) {
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsLessThan()) {
+ set_error(t, "missing < for array declaration");
+ return nullptr;
+ }
+
+ auto subtype = type_decl();
+ if (has_error())
+ return nullptr;
+ if (subtype == nullptr) {
+ set_error(peek(), "invalid type for array declaration");
+ return nullptr;
+ }
+
+ t = next();
+ size_t size = 0;
+ if (t.IsComma()) {
+ t = next();
+ if (!t.IsIntLiteral()) {
+ set_error(t, "missing size of array declaration");
+ return nullptr;
+ }
+ if (t.to_i32() <= 0) {
+ set_error(t, "invalid size for array declaration");
+ return nullptr;
+ }
+ size = static_cast<size_t>(t.to_i32());
+ t = next();
+ }
+ if (!t.IsGreaterThan()) {
+ set_error(t, "missing > for array declaration");
+ return nullptr;
+ }
+
+ return TypeManager::Instance()->Get(
+ std::make_unique<ast::type::ArrayType>(subtype, size));
+}
+
+ast::type::Type* ParserImpl::type_decl_matrix(Token t) {
+ next(); // Consume the peek
+
+ size_t rows = 2;
+ size_t columns = 2;
+ if (t.IsMat3x2() || t.IsMat3x3() || t.IsMat3x4()) {
+ rows = 3;
+ } else if (t.IsMat4x2() || t.IsMat4x3() || t.IsMat4x4()) {
+ rows = 4;
+ }
+ if (t.IsMat2x3() || t.IsMat3x3() || t.IsMat4x3()) {
+ columns = 3;
+ } else if (t.IsMat2x4() || t.IsMat3x4() || t.IsMat4x4()) {
+ columns = 4;
+ }
+
+ t = next();
+ if (!t.IsLessThan()) {
+ set_error(t, "missing < for matrix");
+ return nullptr;
+ }
+
+ auto subtype = type_decl();
+ if (has_error())
+ return nullptr;
+ if (subtype == nullptr) {
+ set_error(peek(), "unable to determine subtype for matrix");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsGreaterThan()) {
+ set_error(t, "missing > for matrix");
+ return nullptr;
+ }
+
+ return TypeManager::Instance()->Get(
+ std::make_unique<ast::type::MatrixType>(subtype, rows, columns));
+}
+
+// storage_class
+// : INPUT
+// | OUTPUT
+// | UNIFORM
+// | WORKGROUP
+// | UNIFORM_CONSTANT
+// | STORAGE_BUFFER
+// | IMAGE
+// | PUSH_CONSTANT
+// | PRIVATE
+// | FUNCTION
+ast::StorageClass ParserImpl::storage_class() {
+ auto t = peek();
+ if (t.IsIn()) {
+ next(); // consume the peek
+ return ast::StorageClass::kInput;
+ }
+ if (t.IsOut()) {
+ next(); // consume the peek
+ return ast::StorageClass::kOutput;
+ }
+ if (t.IsUniform()) {
+ next(); // consume the peek
+ return ast::StorageClass::kUniform;
+ }
+ if (t.IsWorkgroup()) {
+ next(); // consume the peek
+ return ast::StorageClass::kWorkgroup;
+ }
+ if (t.IsUniformConstant()) {
+ next(); // consume the peek
+ return ast::StorageClass::kUniformConstant;
+ }
+ if (t.IsStorageBuffer()) {
+ next(); // consume the peek
+ return ast::StorageClass::kStorageBuffer;
+ }
+ if (t.IsImage()) {
+ next(); // consume the peek
+ return ast::StorageClass::kImage;
+ }
+ if (t.IsPushConstant()) {
+ next(); // consume the peek
+ return ast::StorageClass::kPushConstant;
+ }
+ if (t.IsPrivate()) {
+ next(); // consume the peek
+ return ast::StorageClass::kPrivate;
+ }
+ if (t.IsFunction()) {
+ next(); // consume the peek
+ return ast::StorageClass::kFunction;
+ }
+ return ast::StorageClass::kNone;
+}
+
+// struct_decl
+// : struct_decoration_decl? STRUCT struct_body_decl
+std::unique_ptr<ast::type::StructType> ParserImpl::struct_decl() {
+ auto t = peek();
+ auto source = t.source();
+
+ auto deco = struct_decoration_decl();
+ if (has_error())
+ return nullptr;
+
+ t = next();
+ if (!t.IsStruct()) {
+ set_error(t, "missing struct declaration");
+ return nullptr;
+ }
+
+ t = peek();
+ if (!t.IsBracketLeft()) {
+ set_error(t, "missing { for struct declaration");
+ return nullptr;
+ }
+
+ auto body = struct_body_decl();
+ if (has_error()) {
+ return nullptr;
+ }
+
+ return std::make_unique<ast::type::StructType>(
+ std::make_unique<ast::Struct>(source, deco, std::move(body)));
+}
+
+// struct_decoration_decl
+// : ATTR_LEFT struct_decoration ATTR_RIGHT
+ast::StructDecoration ParserImpl::struct_decoration_decl() {
+ auto t = peek();
+ if (!t.IsAttrLeft())
+ return ast::StructDecoration::kNone;
+
+ next(); // Consume the peek
+
+ auto deco = struct_decoration();
+ if (has_error())
+ return ast::StructDecoration::kNone;
+ if (deco == ast::StructDecoration::kNone) {
+ set_error(peek(), "unknown struct decoration");
+ return ast::StructDecoration::kNone;
+ }
+
+ t = next();
+ if (!t.IsAttrRight()) {
+ set_error(t, "missing ]] for struct decoration");
+ return ast::StructDecoration::kNone;
+ }
+
+ return deco;
+}
+
+// struct_decoration
+// : BLOCK
+ast::StructDecoration ParserImpl::struct_decoration() {
+ auto t = peek();
+ if (t.IsBlock()) {
+ next(); // Consume the peek
+ return ast::StructDecoration::kBlock;
+ }
+ return ast::StructDecoration::kNone;
+}
+
+// struct_body_decl
+// : BRACKET_LEFT struct_member* BRACKET_RIGHT
+std::vector<std::unique_ptr<ast::StructMember>> ParserImpl::struct_body_decl() {
+ auto t = peek();
+ if (!t.IsBracketLeft())
+ return {};
+
+ next(); // Consume the peek
+
+ t = peek();
+ if (t.IsBracketRight())
+ return {};
+
+ std::vector<std::unique_ptr<ast::StructMember>> members;
+ for (;;) {
+ auto mem = struct_member();
+ if (has_error())
+ return {};
+ if (mem == nullptr) {
+ set_error(peek(), "invalid struct member");
+ return {};
+ }
+
+ members.push_back(std::move(mem));
+
+ t = peek();
+ if (t.IsBracketRight() || t.IsEof())
+ break;
+ }
+
+ t = next();
+ if (!t.IsBracketRight()) {
+ set_error(t, "missing } for struct declaration");
+ return {};
+ }
+
+ return members;
+}
+
+// struct_member
+// : struct_member_decoration_decl variable_ident_decl SEMICOLON
+std::unique_ptr<ast::StructMember> ParserImpl::struct_member() {
+ auto t = peek();
+ auto source = t.source();
+
+ auto decos = struct_member_decoration_decl();
+ if (has_error())
+ return nullptr;
+
+ std::string name;
+ ast::type::Type* type;
+ std::tie(name, type) = variable_ident_decl();
+ if (has_error())
+ return nullptr;
+ if (name == "" || type == nullptr) {
+ set_error(peek(), "invalid identifier declaration");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ; for struct member");
+ return nullptr;
+ }
+
+ return std::make_unique<ast::StructMember>(source, name, type,
+ std::move(decos));
+}
+
+// struct_member_decoration_decl
+// :
+// | ATTR_LEFT (struct_member_decoration COMMA)*
+// struct_member_decoration ATTR_RIGHT
+std::vector<std::unique_ptr<ast::StructMemberDecoration>>
+ParserImpl::struct_member_decoration_decl() {
+ auto t = peek();
+ if (!t.IsAttrLeft())
+ return {};
+
+ next(); // Consume the peek
+
+ t = peek();
+ if (t.IsAttrRight()) {
+ set_error(t, "empty struct member decoration found");
+ return {};
+ }
+
+ std::vector<std::unique_ptr<ast::StructMemberDecoration>> decos;
+ bool found_offset = false;
+ for (;;) {
+ auto deco = struct_member_decoration();
+ if (has_error())
+ return {};
+ if (deco == nullptr)
+ break;
+
+ if (deco->IsOffset()) {
+ if (found_offset) {
+ set_error(peek(), "duplicate offset decoration found");
+ return {};
+ }
+ found_offset = true;
+ }
+ decos.push_back(std::move(deco));
+
+ t = next();
+ if (!t.IsComma())
+ break;
+ }
+
+ if (!t.IsAttrRight()) {
+ set_error(t, "missing ]] for struct member decoration");
+ return {};
+ }
+ return decos;
+}
+
+// struct_member_decoration
+// : OFFSET INT_LITERAL
+std::unique_ptr<ast::StructMemberDecoration>
+ParserImpl::struct_member_decoration() {
+ auto t = peek();
+ if (!t.IsOffset())
+ return nullptr;
+
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsIntLiteral()) {
+ set_error(t, "invalid value for offset decoration");
+ return nullptr;
+ }
+ int32_t val = t.to_i32();
+ if (val < 0) {
+ set_error(t, "offset value must be >= 0");
+ return nullptr;
+ }
+
+ return std::make_unique<ast::StructMemberOffsetDecoration>(
+ static_cast<size_t>(val));
+}
+
+// function_decl
+// : function_header body_stmt
+std::unique_ptr<ast::Function> ParserImpl::function_decl() {
+ auto f = function_header();
+ if (has_error())
+ return nullptr;
+ if (f == nullptr)
+ return nullptr;
+
+ auto body = body_stmt();
+ if (has_error())
+ return nullptr;
+
+ f->set_body(std::move(body));
+ return f;
+}
+
+// function_type_decl
+// : type_decl
+// | VOID
+ast::type::Type* ParserImpl::function_type_decl() {
+ auto tm = TypeManager::Instance();
+
+ auto t = peek();
+ if (t.IsVoid()) {
+ next(); // Consume the peek
+ return tm->Get(std::make_unique<ast::type::VoidType>());
+ }
+ return type_decl();
+}
+
+// function_header
+// : FN IDENT PAREN_LEFT param_list PAREN_RIGHT ARROW function_type_decl
+std::unique_ptr<ast::Function> ParserImpl::function_header() {
+ auto t = peek();
+ if (!t.IsFn())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "missing identifier for function");
+ return nullptr;
+ }
+ auto name = t.to_str();
+
+ t = next();
+ if (!t.IsParenLeft()) {
+ set_error(t, "missing ( for function declaration");
+ return nullptr;
+ }
+
+ auto params = param_list();
+ if (has_error())
+ return nullptr;
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "missing ) for function declaration");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsArrow()) {
+ set_error(t, "missing -> for function declaration");
+ return nullptr;
+ }
+
+ auto type = function_type_decl();
+ if (has_error())
+ return nullptr;
+ if (type == nullptr) {
+ set_error(peek(), "unable to determine function return type");
+ return nullptr;
+ }
+
+ return std::make_unique<ast::Function>(source, name, std::move(params), type);
+}
+
+// param_list
+// :
+// | (variable_ident_decl COMMA)* variable_ident_decl
+std::vector<std::unique_ptr<ast::Variable>> ParserImpl::param_list() {
+ auto t = peek();
+ auto source = t.source();
+
+ std::vector<std::unique_ptr<ast::Variable>> ret;
+
+ std::string name;
+ ast::type::Type* type;
+ std::tie(name, type) = variable_ident_decl();
+ if (has_error())
+ return {};
+ if (name == "" || type == nullptr)
+ return {};
+
+ for (;;) {
+ ret.push_back(std::make_unique<ast::Variable>(
+ source, name, ast::StorageClass::kNone, type));
+
+ t = peek();
+ if (!t.IsComma())
+ break;
+
+ source = t.source();
+ next(); // Consume the peek
+
+ std::tie(name, type) = variable_ident_decl();
+ if (has_error())
+ return {};
+ if (name == "" || type == nullptr) {
+ set_error(t, "found , but no variable declaration");
+ return {};
+ }
+ }
+
+ return ret;
+}
+
+// entry_point_decl
+// : ENTRY_POINT pipeline_stage EQUAL IDENT
+// | ENTRY_POINT pipeline_stage AS STRING_LITERAL EQUAL IDENT
+// | ENTRY_POINT pipeline_stage AS IDENT EQUAL IDENT
+std::unique_ptr<ast::EntryPoint> ParserImpl::entry_point_decl() {
+ auto t = peek();
+ auto source = t.source();
+ if (!t.IsEntryPoint())
+ return nullptr;
+
+ next(); // Consume the peek
+
+ auto stage = pipeline_stage();
+ if (has_error())
+ return nullptr;
+ if (stage == ast::PipelineStage::kNone) {
+ set_error(peek(), "missing pipeline stage for entry point");
+ return nullptr;
+ }
+
+ t = next();
+ std::string name;
+ if (t.IsAs()) {
+ t = next();
+ if (t.IsStringLiteral()) {
+ name = t.to_str();
+ } else if (t.IsIdentifier()) {
+ name = t.to_str();
+ } else {
+ set_error(t, "invalid name for entry point");
+ return nullptr;
+ }
+ t = next();
+ }
+
+ if (!t.IsEqual()) {
+ set_error(t, "missing = for entry point");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "invalid function name for entry point");
+ return nullptr;
+ }
+ auto fn_name = t.to_str();
+
+ // Set the name to the function name if it isn't provided
+ if (name.length() == 0)
+ name = fn_name;
+
+ return std::make_unique<ast::EntryPoint>(source, stage, name, fn_name);
+}
+
+// pipeline_stage
+// : VERTEX
+// | FRAGMENT
+// | COMPUTE
+ast::PipelineStage ParserImpl::pipeline_stage() {
+ auto t = peek();
+ if (t.IsVertex()) {
+ next(); // consume the peek
+ return ast::PipelineStage::kVertex;
+ }
+ if (t.IsFragment()) {
+ next(); // consume the peek
+ return ast::PipelineStage::kFragment;
+ }
+ if (t.IsCompute()) {
+ next(); // consume the peek
+ return ast::PipelineStage::kCompute;
+ }
+ return ast::PipelineStage::kNone;
+}
+
+// body_stmt
+// : BRACKET_LEFT statements BRACKET_RIGHT
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::body_stmt() {
+ auto t = peek();
+ if (!t.IsBracketLeft())
+ return {};
+
+ next(); // Consume the peek
+
+ auto stmts = statements();
+ if (has_error())
+ return {};
+
+ t = next();
+ if (!t.IsBracketRight()) {
+ set_error(t, "missing }");
+ return {};
+ }
+
+ return stmts;
+}
+
+// paren_rhs_stmt
+// : PAREN_LEFT logical_or_expression PAREN_RIGHT
+std::unique_ptr<ast::Expression> ParserImpl::paren_rhs_stmt() {
+ auto t = peek();
+ if (!t.IsParenLeft()) {
+ set_error(t, "expected (");
+ return nullptr;
+ }
+ next(); // Consume the peek
+
+ auto expr = logical_or_expression();
+ if (has_error())
+ return nullptr;
+ if (expr == nullptr) {
+ set_error(peek(), "unable to parse expression");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "expected )");
+ return nullptr;
+ }
+
+ return expr;
+}
+
+// statements
+// : statement*
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::statements() {
+ std::vector<std::unique_ptr<ast::Statement>> ret;
+
+ for (;;) {
+ auto stmt = statement();
+ if (has_error())
+ return {};
+ if (stmt == nullptr)
+ break;
+
+ ret.push_back(std::move(stmt));
+ }
+
+ return ret;
+}
+
+// statement
+// : SEMICOLON
+// | RETURN logical_or_expression SEMICOLON
+// | if_stmt
+// | unless_stmt
+// | regardless_stmt
+// | switch_stmt
+// | loop_stmt
+// | variable_stmt SEMICOLON
+// | break_stmt SEMICOLON
+// | continue_stmt SEMICOLON
+// | KILL SEMICOLON
+// | NOP SEMICOLON
+// | assignment_expression SEMICOLON
+std::unique_ptr<ast::Statement> ParserImpl::statement() {
+ auto t = peek();
+ if (t.IsSemicolon()) {
+ next(); // Consume the peek
+ return statement();
+ }
+
+ if (t.IsReturn()) {
+ auto source = t.source();
+ next(); // Consume the peek
+
+ t = peek();
+
+ std::unique_ptr<ast::Expression> expr = nullptr;
+ if (!t.IsSemicolon()) {
+ expr = logical_or_expression();
+ if (has_error())
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ;");
+ return nullptr;
+ }
+ return std::make_unique<ast::ReturnStatement>(source, std::move(expr));
+ }
+
+ auto stmt_if = if_stmt();
+ if (has_error())
+ return nullptr;
+ if (stmt_if != nullptr)
+ return stmt_if;
+
+ auto unless = unless_stmt();
+ if (has_error())
+ return nullptr;
+ if (unless != nullptr)
+ return unless;
+
+ auto regardless = regardless_stmt();
+ if (has_error())
+ return nullptr;
+ if (regardless != nullptr)
+ return regardless;
+
+ auto sw = switch_stmt();
+ if (has_error())
+ return nullptr;
+ if (sw != nullptr)
+ return sw;
+
+ auto loop = loop_stmt();
+ if (has_error())
+ return nullptr;
+ if (loop != nullptr)
+ return loop;
+
+ auto var = variable_stmt();
+ if (has_error())
+ return nullptr;
+ if (var != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ;");
+ return nullptr;
+ }
+ return var;
+ }
+
+ auto b = break_stmt();
+ if (has_error())
+ return nullptr;
+ if (b != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ;");
+ return nullptr;
+ }
+ return b;
+ }
+
+ auto cont = continue_stmt();
+ if (has_error())
+ return nullptr;
+ if (cont != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ;");
+ return nullptr;
+ }
+ return cont;
+ }
+
+ if (t.IsKill()) {
+ auto source = t.source();
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ;");
+ return nullptr;
+ }
+ return std::make_unique<ast::KillStatement>(source);
+ }
+
+ if (t.IsNop()) {
+ auto source = t.source();
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ;");
+ return nullptr;
+ }
+ return std::make_unique<ast::NopStatement>(source);
+ }
+
+ auto assign = assignment_stmt();
+ if (has_error())
+ return nullptr;
+ if (assign != nullptr) {
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ;");
+ return nullptr;
+ }
+ return assign;
+ }
+
+ return nullptr;
+}
+
+// break_stmt
+// : BREAK ({IF | UNLESS} paren_rhs_stmt)?
+std::unique_ptr<ast::BreakStatement> ParserImpl::break_stmt() {
+ auto t = peek();
+ if (!t.IsBreak())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ ast::StatementCondition condition = ast::StatementCondition::kNone;
+ std::unique_ptr<ast::Expression> conditional = nullptr;
+
+ t = peek();
+ if (t.IsIf() || t.IsUnless()) {
+ next(); // Consume the peek
+
+ if (t.IsIf())
+ condition = ast::StatementCondition::kIf;
+ else
+ condition = ast::StatementCondition::kUnless;
+
+ conditional = paren_rhs_stmt();
+ if (has_error())
+ return nullptr;
+ if (conditional == nullptr) {
+ set_error(peek(), "unable to parse conditional statement");
+ return nullptr;
+ }
+ }
+
+ return std::make_unique<ast::BreakStatement>(source, condition,
+ std::move(conditional));
+}
+
+// continue_stmt
+// : CONTINUE ({IF | UNLESS} paren_rhs_stmt)?
+std::unique_ptr<ast::ContinueStatement> ParserImpl::continue_stmt() {
+ auto t = peek();
+ if (!t.IsContinue())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ ast::StatementCondition condition = ast::StatementCondition::kNone;
+ std::unique_ptr<ast::Expression> conditional = nullptr;
+
+ t = peek();
+ if (t.IsIf() || t.IsUnless()) {
+ next(); // Consume the peek
+
+ if (t.IsIf())
+ condition = ast::StatementCondition::kIf;
+ else
+ condition = ast::StatementCondition::kUnless;
+
+ conditional = paren_rhs_stmt();
+ if (has_error())
+ return nullptr;
+ if (conditional == nullptr) {
+ set_error(peek(), "unable to parse conditional statement");
+ return nullptr;
+ }
+ }
+
+ return std::make_unique<ast::ContinueStatement>(source, condition,
+ std::move(conditional));
+}
+
+// variable_stmt
+// : variable_decl
+// | variable_decl EQUAL logical_or_expression
+// | CONST variable_ident_decl EQUAL logical_or_expression
+std::unique_ptr<ast::VariableStatement> ParserImpl::variable_stmt() {
+ auto t = peek();
+ auto source = t.source();
+ if (t.IsConst()) {
+ next(); // Consume the peek
+
+ std::string name;
+ ast::type::Type* type;
+ std::tie(name, type) = variable_ident_decl();
+ if (has_error())
+ return nullptr;
+ if (name == "" || type == nullptr) {
+ set_error(peek(), "unable to parse variable declaration");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsEqual()) {
+ set_error(t, "missing = for constant declaration");
+ return nullptr;
+ }
+
+ auto initializer = logical_or_expression();
+ if (has_error())
+ return nullptr;
+ if (initializer == nullptr) {
+ set_error(peek(), "missing initializer for const declaration");
+ return nullptr;
+ }
+
+ auto var = std::make_unique<ast::Variable>(source, name,
+ ast::StorageClass::kNone, type);
+ var->set_is_const(true);
+ var->set_initializer(std::move(initializer));
+
+ return std::make_unique<ast::VariableStatement>(source, std::move(var));
+ }
+
+ auto var = variable_decl();
+ if (has_error())
+ return nullptr;
+ if (var == nullptr)
+ return nullptr;
+
+ t = peek();
+ if (t.IsEqual()) {
+ next(); // Consume the peek
+ auto initializer = logical_or_expression();
+ if (has_error())
+ return nullptr;
+ if (initializer == nullptr) {
+ set_error(peek(), "missing initializer for variable declaration");
+ return nullptr;
+ }
+ var->set_initializer(std::move(initializer));
+ }
+
+ return std::make_unique<ast::VariableStatement>(source, std::move(var));
+}
+
+// if_stmt
+// : IF paren_rhs_stmt body_stmt
+// {(elseif_stmt else_stmt?) | (else_stmt premerge_stmt?)}
+std::unique_ptr<ast::IfStatement> ParserImpl::if_stmt() {
+ auto t = peek();
+ if (!t.IsIf())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto condition = paren_rhs_stmt();
+ if (has_error())
+ return nullptr;
+ if (condition == nullptr) {
+ set_error(peek(), "unable to parse if condition");
+ return nullptr;
+ }
+
+ t = peek();
+ if (!t.IsBracketLeft()) {
+ set_error(t, "missing {");
+ return nullptr;
+ }
+
+ auto body = body_stmt();
+ if (has_error())
+ return nullptr;
+
+ auto elseif = elseif_stmt();
+ if (has_error())
+ return nullptr;
+
+ auto el = else_stmt();
+ if (has_error())
+ return nullptr;
+
+ auto stmt = std::make_unique<ast::IfStatement>(source, std::move(condition),
+ std::move(body));
+ if (el != nullptr) {
+ if (elseif.size() == 0) {
+ auto premerge = premerge_stmt();
+ if (has_error())
+ return nullptr;
+
+ stmt->set_premerge(std::move(premerge));
+ }
+ elseif.push_back(std::move(el));
+ }
+ stmt->set_else_statements(std::move(elseif));
+
+ return stmt;
+}
+
+// elseif_stmt
+// : ELSE_IF paren_rhs_stmt body_stmt elseif_stmt?
+std::vector<std::unique_ptr<ast::ElseStatement>> ParserImpl::elseif_stmt() {
+ auto t = peek();
+ if (!t.IsElseIf())
+ return {};
+
+ std::vector<std::unique_ptr<ast::ElseStatement>> ret;
+ for (;;) {
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto condition = paren_rhs_stmt();
+ if (has_error())
+ return {};
+ if (condition == nullptr) {
+ set_error(peek(), "unable to parse condition expression");
+ return {};
+ }
+
+ t = peek();
+ if (!t.IsBracketLeft()) {
+ set_error(t, "missing {");
+ return {};
+ }
+
+ auto body = body_stmt();
+ if (has_error())
+ return {};
+
+ ret.push_back(std::make_unique<ast::ElseStatement>(
+ source, std::move(condition), std::move(body)));
+
+ t = peek();
+ if (!t.IsElseIf())
+ break;
+ }
+
+ return ret;
+}
+
+// else_stmt
+// : ELSE body_stmt
+std::unique_ptr<ast::ElseStatement> ParserImpl::else_stmt() {
+ auto t = peek();
+ if (!t.IsElse())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ t = peek();
+ if (!t.IsBracketLeft()) {
+ set_error(t, "missing {");
+ return nullptr;
+ }
+
+ auto body = body_stmt();
+ if (has_error())
+ return nullptr;
+
+ return std::make_unique<ast::ElseStatement>(source, std::move(body));
+}
+
+// premerge_stmt
+// : PREMERGE body_stmt
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::premerge_stmt() {
+ auto t = peek();
+ if (!t.IsPremerge())
+ return {};
+
+ next(); // Consume the peek
+ return body_stmt();
+}
+
+// unless_stmt
+// : UNLESS paren_rhs_stmt body_stmt
+std::unique_ptr<ast::UnlessStatement> ParserImpl::unless_stmt() {
+ auto t = peek();
+ if (!t.IsUnless())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto condition = paren_rhs_stmt();
+ if (has_error())
+ return nullptr;
+ if (condition == nullptr) {
+ set_error(peek(), "unable to parse unless condition");
+ return nullptr;
+ }
+
+ auto body = body_stmt();
+ if (has_error())
+ return nullptr;
+
+ return std::make_unique<ast::UnlessStatement>(source, std::move(condition),
+ std::move(body));
+}
+
+// regardless_stmt
+// : REGARDLESS paren_rhs_stmt body_stmt
+std::unique_ptr<ast::RegardlessStatement> ParserImpl::regardless_stmt() {
+ auto t = peek();
+ if (!t.IsRegardless())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto condition = paren_rhs_stmt();
+ if (has_error())
+ return nullptr;
+ if (condition == nullptr) {
+ set_error(peek(), "unable to parse regardless condition");
+ return nullptr;
+ }
+
+ auto body = body_stmt();
+ if (has_error())
+ return nullptr;
+
+ return std::make_unique<ast::RegardlessStatement>(
+ source, std::move(condition), std::move(body));
+}
+
+// switch_stmt
+// : SWITCH paren_rhs_stmt BRACKET_LEFT switch_body+ BRACKET_RIGHT
+std::unique_ptr<ast::SwitchStatement> ParserImpl::switch_stmt() {
+ auto t = peek();
+ if (!t.IsSwitch())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto condition = paren_rhs_stmt();
+ if (has_error())
+ return nullptr;
+ if (condition == nullptr) {
+ set_error(peek(), "unable to parse switch expression");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsBracketLeft()) {
+ set_error(t, "missing { for switch statement");
+ return nullptr;
+ }
+
+ std::vector<std::unique_ptr<ast::CaseStatement>> body;
+ for (;;) {
+ auto stmt = switch_body();
+ if (has_error())
+ return nullptr;
+ if (stmt == nullptr)
+ break;
+
+ body.push_back(std::move(stmt));
+ }
+
+ t = next();
+ if (!t.IsBracketRight()) {
+ set_error(t, "missing } for switch statement");
+ return nullptr;
+ }
+ return std::make_unique<ast::SwitchStatement>(source, std::move(condition),
+ std::move(body));
+}
+
+// switch_body
+// : CASE const_literal COLON BRACKET_LEFT case_body BRACKET_RIGHT
+// | DEFAULT COLON BRACKET_LEFT case_body BRACKET_RIGHT
+std::unique_ptr<ast::CaseStatement> ParserImpl::switch_body() {
+ auto t = peek();
+ if (!t.IsCase() && !t.IsDefault())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto stmt = std::make_unique<ast::CaseStatement>();
+ stmt->set_source(source);
+ if (t.IsCase()) {
+ auto cond = const_literal();
+ if (has_error())
+ return nullptr;
+ if (cond == nullptr) {
+ set_error(peek(), "unable to parse case conditional");
+ return nullptr;
+ }
+ stmt->set_condition(std::move(cond));
+ }
+
+ t = next();
+ if (!t.IsColon()) {
+ set_error(t, "missing : for case statement");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsBracketLeft()) {
+ set_error(t, "missing { for case statement");
+ return nullptr;
+ }
+
+ auto body = case_body();
+ if (has_error())
+ return nullptr;
+
+ stmt->set_body(std::move(body));
+
+ t = next();
+ if (!t.IsBracketRight()) {
+ set_error(t, "missing } for case statement");
+ return nullptr;
+ }
+
+ return stmt;
+}
+
+// case_body
+// :
+// | statement case_body
+// | FALLTHROUGH SEMICOLON
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::case_body() {
+ std::vector<std::unique_ptr<ast::Statement>> ret;
+ for (;;) {
+ auto t = peek();
+ if (t.IsFallthrough()) {
+ auto source = t.source();
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsSemicolon()) {
+ set_error(t, "missing ;");
+ return {};
+ }
+
+ ret.push_back(std::make_unique<ast::FallthroughStatement>(source));
+ break;
+ }
+
+ auto stmt = statement();
+ if (has_error())
+ return {};
+ if (stmt == nullptr)
+ break;
+
+ ret.push_back(std::move(stmt));
+ }
+
+ return ret;
+}
+
+// loop_stmt
+// : LOOP BRACKET_LEFT statements continuing_stmt? BRACKET_RIGHT
+std::unique_ptr<ast::LoopStatement> ParserImpl::loop_stmt() {
+ auto t = peek();
+ if (!t.IsLoop())
+ return nullptr;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsBracketLeft()) {
+ set_error(t, "missing { for loop");
+ return nullptr;
+ }
+
+ auto body = statements();
+ if (has_error())
+ return nullptr;
+
+ auto continuing = continuing_stmt();
+ if (has_error())
+ return nullptr;
+
+ t = next();
+ if (!t.IsBracketRight()) {
+ set_error(t, "missing } for loop");
+ return nullptr;
+ }
+
+ return std::make_unique<ast::LoopStatement>(source, std::move(body),
+ std::move(continuing));
+}
+
+// continuing_stmt
+// : CONTINUING body_stmt
+std::vector<std::unique_ptr<ast::Statement>> ParserImpl::continuing_stmt() {
+ auto t = peek();
+ if (!t.IsContinuing())
+ return {};
+
+ next(); // Consume the peek
+ return body_stmt();
+}
+
+// const_literal
+// : INT_LITERAL
+// | UINT_LITERAL
+// | FLOAT_LITERAL
+// | TRUE
+// | FALSE
+std::unique_ptr<ast::Literal> ParserImpl::const_literal() {
+ auto t = peek();
+ if (t.IsTrue()) {
+ next(); // Consume the peek
+ return std::make_unique<ast::BoolLiteral>(true);
+ }
+ if (t.IsFalse()) {
+ next(); // Consume the peek
+ return std::make_unique<ast::BoolLiteral>(false);
+ }
+ if (t.IsIntLiteral()) {
+ next(); // Consume the peek
+ return std::make_unique<ast::IntLiteral>(t.to_i32());
+ }
+ if (t.IsUintLiteral()) {
+ next(); // Consume the peek
+ return std::make_unique<ast::UintLiteral>(t.to_u32());
+ }
+ if (t.IsFloatLiteral()) {
+ next(); // Consume the peek
+ return std::make_unique<ast::FloatLiteral>(t.to_f32());
+ }
+ return nullptr;
+}
+
+// const_expr
+// : type_decl PAREN_LEFT (const_expr COMMA)? const_expr PAREN_RIGHT
+// | const_literal
+std::unique_ptr<ast::InitializerExpression> ParserImpl::const_expr() {
+ auto t = peek();
+ auto source = t.source();
+
+ auto type = type_decl();
+ if (type != nullptr) {
+ t = next();
+ if (!t.IsParenLeft()) {
+ set_error(t, "missing ( for type initializer");
+ return nullptr;
+ }
+
+ std::vector<std::unique_ptr<ast::Expression>> params;
+ auto param = const_expr();
+ if (has_error())
+ return nullptr;
+ if (param == nullptr) {
+ set_error(peek(), "unable to parse constant expression");
+ return nullptr;
+ }
+ params.push_back(std::move(param));
+ for (;;) {
+ t = peek();
+ if (!t.IsComma())
+ break;
+
+ next(); // Consume the peek
+
+ param = const_expr();
+ if (has_error())
+ return nullptr;
+ if (param == nullptr) {
+ set_error(peek(), "unable to parse constant expression");
+ return nullptr;
+ }
+ params.push_back(std::move(param));
+ }
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "missing ) for type initializer");
+ return nullptr;
+ }
+ return std::make_unique<ast::TypeInitializerExpression>(source, type,
+ std::move(params));
+ }
+
+ auto lit = const_literal();
+ if (has_error())
+ return nullptr;
+ if (lit == nullptr) {
+ set_error(peek(), "unable to parse const literal");
+ return nullptr;
+ }
+ return std::make_unique<ast::ConstInitializerExpression>(source,
+ std::move(lit));
+}
+
+// primary_expression
+// : (IDENT NAMESPACE)* IDENT
+// | type_decl PAREN_LEFT argument_expression_list PAREN_RIGHT
+// | const_literal
+// | paren_rhs_stmt
+// | CAST LESS_THAN type_decl GREATER_THAN paren_rhs_stmt
+// | AS LESS_THAN type_decl GREATER_THAN paren_rhs_stmt
+std::unique_ptr<ast::Expression> ParserImpl::primary_expression() {
+ auto t = peek();
+ auto source = t.source();
+
+ auto lit = const_literal();
+ if (has_error())
+ return nullptr;
+ if (lit != nullptr) {
+ return std::make_unique<ast::ConstInitializerExpression>(source,
+ std::move(lit));
+ }
+
+ t = peek();
+ if (t.IsParenLeft()) {
+ auto paren = paren_rhs_stmt();
+ if (has_error())
+ return nullptr;
+
+ return paren;
+ }
+
+ if (t.IsCast() || t.IsAs()) {
+ auto src = t;
+
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsLessThan()) {
+ set_error(t, "missing < for " + src.to_name() + " expression");
+ return nullptr;
+ }
+
+ auto type = type_decl();
+ if (has_error())
+ return nullptr;
+ if (type == nullptr) {
+ set_error(peek(), "missing type for " + src.to_name() + " expression");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsGreaterThan()) {
+ set_error(t, "missing > for " + src.to_name() + " expression");
+ return nullptr;
+ }
+
+ auto params = paren_rhs_stmt();
+ if (has_error())
+ return nullptr;
+ if (params == nullptr) {
+ set_error(peek(), "unable to parse parameters");
+ return nullptr;
+ }
+
+ if (src.IsCast()) {
+ return std::make_unique<ast::CastExpression>(source, type,
+ std::move(params));
+ } else {
+ return std::make_unique<ast::AsExpression>(source, type,
+ std::move(params));
+ }
+
+ } else if (t.IsIdentifier()) {
+ next(); // Consume the peek
+
+ std::vector<std::string> ident;
+ ident.push_back(t.to_str());
+ for (;;) {
+ t = peek();
+ if (!t.IsNamespace())
+ break;
+
+ next(); // Consume the peek
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "identifier expected");
+ return nullptr;
+ }
+
+ ident.push_back(t.to_str());
+ }
+ return std::make_unique<ast::IdentifierExpression>(source,
+ std::move(ident));
+ }
+
+ auto type = type_decl();
+ if (has_error())
+ return nullptr;
+ if (type != nullptr) {
+ t = next();
+ if (!t.IsParenLeft()) {
+ set_error(t, "missing ( for type initializer");
+ return nullptr;
+ }
+
+ auto params = argument_expression_list();
+ if (has_error())
+ return nullptr;
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "missing ) for type initializer");
+ return nullptr;
+ }
+ return std::make_unique<ast::TypeInitializerExpression>(source, type,
+ std::move(params));
+ }
+ return nullptr;
+}
+
+// argument_expression_list
+// : (logical_or_expression COMMA)* logical_or_expression
+std::vector<std::unique_ptr<ast::Expression>>
+ParserImpl::argument_expression_list() {
+ auto arg = logical_or_expression();
+ if (has_error())
+ return {};
+ if (arg == nullptr) {
+ set_error(peek(), "unable to parse argument expression");
+ return {};
+ }
+
+ std::vector<std::unique_ptr<ast::Expression>> ret;
+ ret.push_back(std::move(arg));
+
+ for (;;) {
+ auto t = peek();
+ if (!t.IsComma())
+ break;
+
+ next(); // Consume the peek
+
+ arg = logical_or_expression();
+ if (has_error())
+ return {};
+ if (arg == nullptr) {
+ set_error(peek(), "unable to parse argument expression after comma");
+ return {};
+ }
+ ret.push_back(std::move(arg));
+ }
+ return ret;
+}
+
+// postfix_expr
+// :
+// | BRACE_LEFT logical_or_expression BRACE_RIGHT postfix_expr
+// | PAREN_LEFT argument_expression_list* PAREN_RIGHT postfix_expr
+// | PERIOD IDENTIFIER postfix_expr
+std::unique_ptr<ast::Expression> ParserImpl::postfix_expr(
+ std::unique_ptr<ast::Expression> prefix) {
+ std::unique_ptr<ast::Expression> expr = nullptr;
+
+ auto t = peek();
+ auto source = t.source();
+ if (t.IsBraceLeft()) {
+ next(); // Consume the peek
+
+ auto param = logical_or_expression();
+ if (has_error())
+ return nullptr;
+ if (param == nullptr) {
+ set_error(peek(), "unable to parse expression inside []");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsBraceRight()) {
+ set_error(t, "missing ] for array accessor");
+ return nullptr;
+ }
+ expr = std::make_unique<ast::ArrayAccessorExpression>(
+ source, std::move(prefix), std::move(param));
+
+ } else if (t.IsParenLeft()) {
+ next(); // Consume the peek
+
+ t = peek();
+ std::vector<std::unique_ptr<ast::Expression>> params;
+ if (!t.IsParenRight() && !t.IsEof()) {
+ params = argument_expression_list();
+ if (has_error())
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "missing ) for call expression");
+ return nullptr;
+ }
+ expr = std::make_unique<ast::CallExpression>(source, std::move(prefix),
+ std::move(params));
+ } else if (t.IsPeriod()) {
+ next(); // Consume the peek
+
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "missing identifier for member accessor");
+ return nullptr;
+ }
+
+ expr = std::make_unique<ast::MemberAccessorExpression>(
+ source, std::move(prefix),
+ std::make_unique<ast::IdentifierExpression>(t.source(), t.to_str()));
+ } else {
+ return prefix;
+ }
+ return postfix_expr(std::move(expr));
+}
+
+// postfix_expression
+// : primary_expression postfix_expr
+std::unique_ptr<ast::Expression> ParserImpl::postfix_expression() {
+ auto prefix = primary_expression();
+ if (has_error())
+ return nullptr;
+ if (prefix == nullptr)
+ return nullptr;
+
+ return postfix_expr(std::move(prefix));
+}
+
+// unary_expression
+// : postfix_expression
+// | MINUS unary_expression
+// | BANG unary_expression
+// | ANY PAREN_LEFT IDENT PAREN_RIGHT
+// | ALL PAREN_LEFT IDENT PAREN_RIGHT
+// | IS_NAN PAREN_LEFT IDENT PAREN_RIGHT
+// | IS_INF PAREN_LEFT IDENT PAREN_RIGHT
+// | IS_FINITE PAREN_LEFT IDENT PAREN_RIGHT
+// | IS_NORMAL PAREN_LEFT IDENT PAREN_RIGHT
+// | DOT PAREN_LEFT IDENT COMMA IDENT PAREN_RIGHT
+// | OUTER_PRODUCT PAREN_LEFT IDENT COMMA IDENT PAREN_RIGHT
+// | DPDX (LESS_THAN derivative_modifier GREATER_THAN)?
+// PAREN_LEFT IDENT PAREN_RIGHT
+// | DPDY (LESS_THAN derivative_modifier GREATER_THAN)?
+// PAREN_LEFT IDENT PAREN_RIGHT
+// | FWIDTH (LESS_THAN derivative_modifier GREATER_THAN)?
+// PAREN_LEFT IDENT PAREN_RIGHT
+// # | unord_greater_than_equal(a, b)
+// # | unord_greater_than(a, b)
+// # | unord_less_than_equal(a, b)
+// # | unord_less_than(a, b)
+// # | unord_not_equal(a, b)
+// # | unord_equal(a, b)
+// # | signed_greater_than_equal(a, b)
+// # | signed_greater_than(a, b)
+// # | signed_less_than_equal(a, b)
+// # | signed_less_than(a, b)
+std::unique_ptr<ast::Expression> ParserImpl::unary_expression() {
+ auto t = peek();
+ auto source = t.source();
+ if (t.IsMinus() || t.IsBang()) {
+ auto name = t.to_name();
+
+ next(); // Consume the peek
+
+ auto op = ast::UnaryOp::kNegation;
+ if (t.IsBang())
+ op = ast::UnaryOp::kNot;
+
+ auto expr = unary_expression();
+ if (has_error())
+ return nullptr;
+ if (expr == nullptr) {
+ set_error(peek(),
+ "unable to parse right side of " + name + " expression");
+ return nullptr;
+ }
+ return std::make_unique<ast::UnaryOpExpression>(source, op,
+ std::move(expr));
+ }
+ if (t.IsAny() || t.IsAll() || t.IsIsNan() || t.IsIsInf() || t.IsIsFinite() ||
+ t.IsIsNormal()) {
+ next(); // Consume the peek
+
+ auto op = ast::UnaryMethod::kAny;
+ if (t.IsAll())
+ op = ast::UnaryMethod::kAll;
+ else if (t.IsIsNan())
+ op = ast::UnaryMethod::kIsNan;
+ else if (t.IsIsInf())
+ op = ast::UnaryMethod::kIsInf;
+ else if (t.IsIsFinite())
+ op = ast::UnaryMethod::kIsFinite;
+ else if (t.IsIsNormal())
+ op = ast::UnaryMethod::kIsNormal;
+
+ t = next();
+ if (!t.IsParenLeft()) {
+ set_error(t, "missing ( for method call");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "missing identifier for method call");
+ return nullptr;
+ }
+ std::vector<std::unique_ptr<ast::Expression>> ident;
+ ident.push_back(
+ std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "missing ) for method call");
+ return nullptr;
+ }
+ return std::make_unique<ast::UnaryMethodExpression>(source, op,
+ std::move(ident));
+ }
+ if (t.IsDot() || t.IsOuterProduct()) {
+ next(); // Consume the peek
+
+ auto op = ast::UnaryMethod::kDot;
+ if (t.IsOuterProduct())
+ op = ast::UnaryMethod::kOuterProduct;
+
+ t = next();
+ if (!t.IsParenLeft()) {
+ set_error(t, "missing ( for method call");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "missing identifier for method call");
+ return nullptr;
+ }
+ std::vector<std::unique_ptr<ast::Expression>> ident;
+ ident.push_back(
+ std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
+
+ t = next();
+ if (!t.IsComma()) {
+ set_error(t, "missing , for method call");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "missing identifier for method call");
+ return nullptr;
+ }
+ ident.push_back(
+ std::make_unique<ast::IdentifierExpression>(source, t.to_str()));
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "missing ) for method call");
+ return nullptr;
+ }
+
+ return std::make_unique<ast::UnaryMethodExpression>(source, op,
+ std::move(ident));
+ }
+ if (t.IsDpdx() || t.IsDpdy() || t.IsFwidth()) {
+ next(); // Consume the peek
+
+ auto op = ast::UnaryDerivative::kDpdx;
+ if (t.IsDpdy())
+ op = ast::UnaryDerivative::kDpdy;
+ else if (t.IsFwidth())
+ op = ast::UnaryDerivative::kFwidth;
+
+ t = next();
+ auto mod = ast::DerivativeModifier::kNone;
+ if (t.IsLessThan()) {
+ mod = derivative_modifier();
+ if (has_error())
+ return nullptr;
+ if (mod == ast::DerivativeModifier::kNone) {
+ set_error(peek(), "unable to parse derivative modifier");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsGreaterThan()) {
+ set_error(t, "missing > for derivative modifier");
+ return nullptr;
+ }
+ t = next();
+ }
+ if (!t.IsParenLeft()) {
+ set_error(t, "missing ( for derivative method");
+ return nullptr;
+ }
+
+ t = next();
+ if (!t.IsIdentifier()) {
+ set_error(t, "missing identifier for derivative method");
+ return nullptr;
+ }
+ auto ident =
+ std::make_unique<ast::IdentifierExpression>(source, t.to_str());
+
+ t = next();
+ if (!t.IsParenRight()) {
+ set_error(t, "missing ) for derivative method");
+ return nullptr;
+ }
+
+ return std::make_unique<ast::UnaryDerivativeExpression>(source, op, mod,
+ std::move(ident));
+ }
+ return postfix_expression();
+}
+
+// derivative_modifier
+// : FINE
+// | COARSE
+ast::DerivativeModifier ParserImpl::derivative_modifier() {
+ auto t = peek();
+ if (t.IsFine()) {
+ next(); // Consume the peek
+ return ast::DerivativeModifier::kFine;
+ }
+ if (t.IsCoarse()) {
+ next(); // Consume the peek
+ return ast::DerivativeModifier::kCoarse;
+ }
+ return ast::DerivativeModifier::kNone;
+}
+
+// multiplicative_expr
+// :
+// | STAR unary_expression multiplicative_expr
+// | FORWARD_SLASH unary_expression multiplicative_expr
+// | MODULO unary_expression multiplicative_expr
+std::unique_ptr<ast::Expression> ParserImpl::multiplicative_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+
+ ast::Relation relation = ast::Relation::kNone;
+ if (t.IsStar())
+ relation = ast::Relation::kMultiply;
+ else if (t.IsForwardSlash())
+ relation = ast::Relation::kDivide;
+ else if (t.IsMod())
+ relation = ast::Relation::kModulo;
+ else
+ return lhs;
+
+ auto source = t.source();
+ auto name = t.to_name();
+ next(); // Consume the peek
+
+ auto rhs = unary_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of " + name + " expression");
+ return nullptr;
+ }
+ return multiplicative_expr(std::make_unique<ast::RelationalExpression>(
+ source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// multiplicative_expression
+// : unary_expression multiplicative_expr
+std::unique_ptr<ast::Expression> ParserImpl::multiplicative_expression() {
+ auto lhs = unary_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return multiplicative_expr(std::move(lhs));
+}
+
+// additive_expr
+// :
+// | PLUS multiplicative_expression additive_expr
+// | MINUS multiplicative_expression additive_expr
+std::unique_ptr<ast::Expression> ParserImpl::additive_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+
+ ast::Relation relation = ast::Relation::kNone;
+ if (t.IsPlus())
+ relation = ast::Relation::kAdd;
+ else if (t.IsMinus())
+ relation = ast::Relation::kSubtract;
+ else
+ return lhs;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto rhs = multiplicative_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of + expression");
+ return nullptr;
+ }
+ return additive_expr(std::make_unique<ast::RelationalExpression>(
+ source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// additive_expression
+// : multiplicative_expression additive_expr
+std::unique_ptr<ast::Expression> ParserImpl::additive_expression() {
+ auto lhs = multiplicative_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return additive_expr(std::move(lhs));
+}
+
+// shift_expr
+// :
+// | LESS_THAN LESS_THAN additive_expression shift_expr
+// | GREATER_THAN GREATER_THAN additive_expression shift_expr
+// | GREATER_THAN GREATER_THAN GREATER_THAN additive_expression shift_expr
+std::unique_ptr<ast::Expression> ParserImpl::shift_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+ auto source = t.source();
+ auto t2 = peek(1);
+ auto t3 = peek(2);
+
+ auto name = "";
+ ast::Relation relation = ast::Relation::kNone;
+ if (t.IsLessThan() && t2.IsLessThan()) {
+ next(); // Consume the t peek
+ next(); // Consume the t2 peek
+ relation = ast::Relation::kShiftLeft;
+ name = "<<";
+ } else if (t.IsGreaterThan() && t2.IsGreaterThan() && t3.IsGreaterThan()) {
+ next(); // Consume the t peek
+ next(); // Consume the t2 peek
+ next(); // Consume the t3 peek
+ relation = ast::Relation::kShiftRightArith;
+ name = ">>>";
+ } else if (t.IsGreaterThan() && t2.IsGreaterThan()) {
+ next(); // Consume the t peek
+ next(); // Consume the t2 peek
+ relation = ast::Relation::kShiftRight;
+ name = ">>";
+ } else {
+ return lhs;
+ }
+
+ auto rhs = additive_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), std::string("unable to parse right side of ") + name +
+ " expression");
+ return nullptr;
+ }
+ return shift_expr(std::make_unique<ast::RelationalExpression>(
+ source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// shift_expression
+// : additive_expression shift_expr
+std::unique_ptr<ast::Expression> ParserImpl::shift_expression() {
+ auto lhs = additive_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return shift_expr(std::move(lhs));
+}
+
+// relational_expr
+// :
+// | LESS_THAN shift_expression relational_expr
+// | GREATER_THAN shift_expression relational_expr
+// | LESS_THAN_EQUAL shift_expression relational_expr
+// | GREATER_THAN_EQUAL shift_expression relational_expr
+std::unique_ptr<ast::Expression> ParserImpl::relational_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+ ast::Relation relation = ast::Relation::kNone;
+ if (t.IsLessThan())
+ relation = ast::Relation::kLessThan;
+ else if (t.IsGreaterThan())
+ relation = ast::Relation::kGreaterThan;
+ else if (t.IsLessThanEqual())
+ relation = ast::Relation::kLessThanEqual;
+ else if (t.IsGreaterThanEqual())
+ relation = ast::Relation::kGreaterThanEqual;
+ else
+ return lhs;
+
+ auto source = t.source();
+ auto name = t.to_name();
+ next(); // Consume the peek
+
+ auto rhs = shift_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of " + name + " expression");
+ return nullptr;
+ }
+ return relational_expr(std::make_unique<ast::RelationalExpression>(
+ source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// relational_expression
+// : shift_expression relational_expr
+std::unique_ptr<ast::Expression> ParserImpl::relational_expression() {
+ auto lhs = shift_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return relational_expr(std::move(lhs));
+}
+
+// equality_expr
+// :
+// | EQUAL_EQUAL relational_expression equality_expr
+// | NOT_EQUAL relational_expression equality_expr
+std::unique_ptr<ast::Expression> ParserImpl::equality_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+ ast::Relation relation = ast::Relation::kNone;
+ if (t.IsEqualEqual())
+ relation = ast::Relation::kEqual;
+ else if (t.IsNotEqual())
+ relation = ast::Relation::kNotEqual;
+ else
+ return lhs;
+
+ auto source = t.source();
+ auto name = t.to_name();
+ next(); // Consume the peek
+
+ auto rhs = relational_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of " + name + " expression");
+ return nullptr;
+ }
+ return equality_expr(std::make_unique<ast::RelationalExpression>(
+ source, relation, std::move(lhs), std::move(rhs)));
+}
+
+// equality_expression
+// : relational_expression equality_expr
+std::unique_ptr<ast::Expression> ParserImpl::equality_expression() {
+ auto lhs = relational_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return equality_expr(std::move(lhs));
+}
+
+// and_expr
+// :
+// | AND equality_expression and_expr
+std::unique_ptr<ast::Expression> ParserImpl::and_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+ if (!t.IsAnd())
+ return lhs;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto rhs = equality_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of & expression");
+ return nullptr;
+ }
+ return and_expr(std::make_unique<ast::RelationalExpression>(
+ source, ast::Relation::kAnd, std::move(lhs), std::move(rhs)));
+}
+
+// and_expression
+// : equality_expression and_expr
+std::unique_ptr<ast::Expression> ParserImpl::and_expression() {
+ auto lhs = equality_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return and_expr(std::move(lhs));
+}
+
+// exclusive_or_expr
+// :
+// | XOR and_expression exclusive_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::exclusive_or_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+ if (!t.IsXor())
+ return lhs;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto rhs = and_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of ^ expression");
+ return nullptr;
+ }
+ return exclusive_or_expr(std::make_unique<ast::RelationalExpression>(
+ source, ast::Relation::kXor, std::move(lhs), std::move(rhs)));
+}
+
+// exclusive_or_expression
+// : and_expression exclusive_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::exclusive_or_expression() {
+ auto lhs = and_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return exclusive_or_expr(std::move(lhs));
+}
+
+// inclusive_or_expr
+// :
+// | OR exclusive_or_expression inclusive_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::inclusive_or_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+ if (!t.IsOr())
+ return lhs;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto rhs = exclusive_or_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of | expression");
+ return nullptr;
+ }
+ return inclusive_or_expr(std::make_unique<ast::RelationalExpression>(
+ source, ast::Relation::kOr, std::move(lhs), std::move(rhs)));
+}
+
+// inclusive_or_expression
+// : exclusive_or_expression inclusive_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::inclusive_or_expression() {
+ auto lhs = exclusive_or_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return inclusive_or_expr(std::move(lhs));
+}
+
+// logical_and_expr
+// :
+// | AND_AND inclusive_or_expression logical_and_expr
+std::unique_ptr<ast::Expression> ParserImpl::logical_and_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+ if (!t.IsAndAnd())
+ return lhs;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto rhs = inclusive_or_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of && expression");
+ return nullptr;
+ }
+ return logical_and_expr(std::make_unique<ast::RelationalExpression>(
+ source, ast::Relation::kLogicalAnd, std::move(lhs), std::move(rhs)));
+}
+
+// logical_and_expression
+// : inclusive_or_expression logical_and_expr
+std::unique_ptr<ast::Expression> ParserImpl::logical_and_expression() {
+ auto lhs = inclusive_or_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return logical_and_expr(std::move(lhs));
+}
+
+// logical_or_expr
+// :
+// | OR_OR logical_and_expression logical_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::logical_or_expr(
+ std::unique_ptr<ast::Expression> lhs) {
+ auto t = peek();
+ if (!t.IsOrOr())
+ return lhs;
+
+ auto source = t.source();
+ next(); // Consume the peek
+
+ auto rhs = logical_and_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of || expression");
+ return nullptr;
+ }
+ return logical_or_expr(std::make_unique<ast::RelationalExpression>(
+ source, ast::Relation::kLogicalOr, std::move(lhs), std::move(rhs)));
+}
+
+// logical_or_expression
+// : logical_and_expression logical_or_expr
+std::unique_ptr<ast::Expression> ParserImpl::logical_or_expression() {
+ auto lhs = logical_and_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ return logical_or_expr(std::move(lhs));
+}
+
+// assignment_stmt
+// : unary_expression EQUAL logical_or_expression
+std::unique_ptr<ast::AssignmentStatement> ParserImpl::assignment_stmt() {
+ auto t = peek();
+ auto source = t.source();
+
+ auto lhs = unary_expression();
+ if (has_error())
+ return nullptr;
+ if (lhs == nullptr)
+ return nullptr;
+
+ t = next();
+ if (!t.IsEqual()) {
+ set_error(t, "missing = for assignment");
+ return nullptr;
+ }
+
+ auto rhs = logical_or_expression();
+ if (has_error())
+ return nullptr;
+ if (rhs == nullptr) {
+ set_error(peek(), "unable to parse right side of assignment");
+ return nullptr;
+ }
+
+ return std::make_unique<ast::AssignmentStatement>(source, std::move(lhs),
+ std::move(rhs));
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
new file mode 100644
index 0000000..7fa3a23
--- /dev/null
+++ b/src/reader/wgsl/parser_impl.h
@@ -0,0 +1,363 @@
+// Copyright 2020 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_READER_WGSL_PARSER_IMPL_H_
+#define SRC_READER_WGSL_PARSER_IMPL_H_
+
+#include <deque>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "src/ast/assignment_statement.h"
+#include "src/ast/builtin.h"
+#include "src/ast/derivative_modifier.h"
+#include "src/ast/entry_point.h"
+#include "src/ast/function.h"
+#include "src/ast/import.h"
+#include "src/ast/initializer_expression.h"
+#include "src/ast/literal.h"
+#include "src/ast/loop_statement.h"
+#include "src/ast/module.h"
+#include "src/ast/pipeline_stage.h"
+#include "src/ast/regardless_statement.h"
+#include "src/ast/statement.h"
+#include "src/ast/storage_class.h"
+#include "src/ast/struct.h"
+#include "src/ast/struct_decoration.h"
+#include "src/ast/struct_member.h"
+#include "src/ast/struct_member_decoration.h"
+#include "src/ast/type/type.h"
+#include "src/ast/unless_statement.h"
+#include "src/ast/variable.h"
+#include "src/ast/variable_decoration.h"
+#include "src/reader/wgsl/token.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+class Lexer;
+
+/// ParserImpl for WGSL source data
+class ParserImpl {
+ public:
+ /// Creates a new parser
+ /// @param input the input string to parse
+ explicit ParserImpl(const std::string& input);
+ ~ParserImpl();
+
+ /// Run the parser
+ /// @returns true if the parse was successful, false otherwise.
+ bool Parse();
+
+ /// @returns true if an error was encountered
+ bool has_error() const { return error_.size() > 0; }
+ /// @returns the parser error string
+ const std::string& error() const { return error_; }
+
+ /// @returns the module. The module in the parser will be reset after this.
+ ast::Module module() { return std::move(module_); }
+
+ /// @returns the next token
+ Token next();
+ /// @returns the next token without advancing
+ Token peek();
+ /// Peeks ahead and returns the token at |idx| head of the current position
+ /// @param idx the index of the token to return
+ /// @returns the token |idx| positions ahead without advancing
+ Token peek(size_t idx);
+ /// Sets the error from |t|
+ /// @param t the token to set the error from
+ void set_error(const Token& t);
+ /// Sets the error from |t| or |msg| if |t| is not in error
+ /// @param t the token to set the error from
+ /// @param msg the error message
+ void set_error(const Token& t, const std::string& msg);
+
+ /// Registers a type alias into the parser
+ /// @param name the alias name
+ /// @param type the alias'd type
+ void register_alias(const std::string& name, ast::type::Type* type);
+ /// Retrieves an aliased type
+ /// @param name The alias name to lookup
+ /// @returns the alias type for |name| or nullptr if not found
+ ast::type::Type* get_alias(const std::string& name);
+
+ /// Parses the `translation_unit` grammar element
+ void translation_unit();
+ /// Parses the `global_decl` grammar element
+ void global_decl();
+ /// Parses the `import_decl grammar element
+ /// @returns the import object or nullptr if an error was encountered
+ std::unique_ptr<ast::Import> import_decl();
+ /// Parses a `global_variable_decl` grammar element
+ /// @returns the variable parsed or nullptr
+ std::unique_ptr<ast::Variable> global_variable_decl();
+ /// Parses a `global_constant_decl` grammar element
+ /// @returns the const object or nullptr
+ std::unique_ptr<ast::Variable> global_constant_decl();
+ /// Parses a `variable_decoration_list` grammar element
+ /// @returns a vector of parsed variable decorations
+ std::vector<std::unique_ptr<ast::VariableDecoration>>
+ variable_decoration_list();
+ /// Parses a `variable_decoration` grammar element
+ /// @returns the variable decoration or nullptr if an error is encountered
+ std::unique_ptr<ast::VariableDecoration> variable_decoration();
+ /// Parses a `builtin_decoration` grammar element
+ /// @returns the builtin or Builtin::kNone if none matched
+ ast::Builtin builtin_decoration();
+ /// Parses a `variable_decl` grammar element
+ /// @returns the parsed variable or nullptr otherwise
+ std::unique_ptr<ast::Variable> variable_decl();
+ /// Parses a `variable_ident_decl` grammar element
+ /// @returns the identifier and type parsed or empty otherwise
+ std::pair<std::string, ast::type::Type*> variable_ident_decl();
+ /// Parses a `variable_storage_decoration` grammar element
+ /// @returns the storage class or StorageClass::kNone if none matched
+ ast::StorageClass variable_storage_decoration();
+ /// Parses a `type_alias` grammar element
+ /// @returns the type alias or nullptr on error
+ ast::type::AliasType* type_alias();
+ /// Parses a `type_decl` grammar element
+ /// @returns the parsed Type or nullptr if none matched. The returned type
+ // is owned by the TypeManager.
+ ast::type::Type* type_decl();
+ /// Parses a `storage_class` grammar element
+ /// @returns the storage class or StorageClass::kNone if none matched
+ ast::StorageClass storage_class();
+ /// Parses a `struct_decl` grammar element
+ /// @returns the struct type or nullptr on error
+ std::unique_ptr<ast::type::StructType> struct_decl();
+ /// Parses a `struct_decoration_decl` grammar element
+ /// @returns the struct decoration or StructDecoraton::kNone
+ ast::StructDecoration struct_decoration_decl();
+ /// Parses a `struct_decoration` grammar element
+ /// @returns the struct decoration or StructDecoraton::kNone if none matched
+ ast::StructDecoration struct_decoration();
+ /// Parses a `struct_body_decl` grammar element
+ /// @returns the struct members
+ std::vector<std::unique_ptr<ast::StructMember>> struct_body_decl();
+ /// Parses a `struct_member` grammar element
+ /// @returns the struct member or nullptr
+ std::unique_ptr<ast::StructMember> struct_member();
+ /// Parses a `struct_member_decoration_decl` grammar element
+ /// @returns the list of decorations
+ std::vector<std::unique_ptr<ast::StructMemberDecoration>>
+ struct_member_decoration_decl();
+ /// Parses a `struct_member_decoration` grammar element
+ /// @returns the decoration or nullptr if none found
+ std::unique_ptr<ast::StructMemberDecoration> struct_member_decoration();
+ /// Parses a `function_decl` grammar element
+ /// @returns the parsed function, nullptr otherwise
+ std::unique_ptr<ast::Function> function_decl();
+ /// Parses a `function_type_decl` grammar element
+ /// @returns the parsed type or nullptr otherwise
+ ast::type::Type* function_type_decl();
+ /// Parses a `function_header` grammar element
+ /// @returns the parsed function nullptr otherwise
+ std::unique_ptr<ast::Function> function_header();
+ /// Parses a `param_list` grammar element
+ /// @returns the parsed variables
+ std::vector<std::unique_ptr<ast::Variable>> param_list();
+ /// Parses a `entry_point_decl` grammar element
+ /// @returns the EntryPoint or nullptr on error
+ std::unique_ptr<ast::EntryPoint> entry_point_decl();
+ /// Parses a `pipeline_stage` grammar element
+ /// @returns the pipeline stage or PipelineStage::kNone if none matched
+ ast::PipelineStage pipeline_stage();
+ /// Parses a `body_stmt` grammar element
+ /// @returns the parsed statements
+ std::vector<std::unique_ptr<ast::Statement>> body_stmt();
+ /// Parses a `paren_rhs_stmt` grammar element
+ /// @returns the parsed element or nullptr
+ std::unique_ptr<ast::Expression> paren_rhs_stmt();
+ /// Parses a `statements` grammar element
+ /// @returns the statements parsed
+ std::vector<std::unique_ptr<ast::Statement>> statements();
+ /// Parses a `statement` grammar element
+ /// @returns the parsed statement or nullptr
+ std::unique_ptr<ast::Statement> statement();
+ /// Parses a `break_stmt` gramamr element
+ /// @returns the parsed statement or nullptr
+ std::unique_ptr<ast::BreakStatement> break_stmt();
+ /// Parses a `continue_stmt` grammar element
+ /// @returns the parsed statement or nullptr
+ std::unique_ptr<ast::ContinueStatement> continue_stmt();
+ /// Parses a `variable_stmt` grammar element
+ /// @returns the parsed variable or nullptr
+ std::unique_ptr<ast::VariableStatement> variable_stmt();
+ /// Parses a `if_stmt` grammar element
+ /// @returns the parsed statement or nullptr
+ std::unique_ptr<ast::IfStatement> if_stmt();
+ /// Parses a `elseif_stmt` grammar element
+ /// @returns the parsed elements
+ std::vector<std::unique_ptr<ast::ElseStatement>> elseif_stmt();
+ /// Parses a `else_stmt` grammar element
+ /// @returns the parsed statement or nullptr
+ std::unique_ptr<ast::ElseStatement> else_stmt();
+ /// Parses a `premerge_stmt` grammar element
+ /// @returns the parsed statements
+ std::vector<std::unique_ptr<ast::Statement>> premerge_stmt();
+ /// Parses a `unless_stmt` grammar element
+ /// @returns the parsed element or nullptr
+ std::unique_ptr<ast::UnlessStatement> unless_stmt();
+ /// Parses a `regardless_stmt` grammar element
+ /// @returns the parsed element or nullptr
+ std::unique_ptr<ast::RegardlessStatement> regardless_stmt();
+ /// Parses a `switch_stmt` grammar element
+ /// @returns the parsed statement or nullptr
+ std::unique_ptr<ast::SwitchStatement> switch_stmt();
+ /// Parses a `switch_body` grammar element
+ /// @returns the parsed statement or nullptr
+ std::unique_ptr<ast::CaseStatement> switch_body();
+ /// Parses a `case_body` grammar element
+ /// @returns the parsed statements
+ std::vector<std::unique_ptr<ast::Statement>> case_body();
+ /// Parses a `loop_stmt` grammar element
+ /// @returns the parsed loop or nullptr
+ std::unique_ptr<ast::LoopStatement> loop_stmt();
+ /// Parses a `continuing_stmt` grammar element
+ /// @returns the parsed statements
+ std::vector<std::unique_ptr<ast::Statement>> continuing_stmt();
+ /// Parses a `const_literal` grammar element
+ /// @returns the const literal parsed or nullptr if none found
+ std::unique_ptr<ast::Literal> const_literal();
+ /// Parses a `const_expr` grammar element
+ /// @returns the parsed initializer expression or nullptr on error
+ std::unique_ptr<ast::InitializerExpression> const_expr();
+ /// Parses a `primary_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> primary_expression();
+ /// Parses a `argument_expression_list` grammar element
+ /// @returns the list of arguments
+ std::vector<std::unique_ptr<ast::Expression>> argument_expression_list();
+ /// Parses the recursive portion of the postfix_expression
+ /// @param prefix the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> postfix_expr(
+ std::unique_ptr<ast::Expression> prefix);
+ /// Parses a `postfix_expression` grammar elment
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> postfix_expression();
+ /// Parses a `unary_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> unary_expression();
+ /// Parses a `derivative_modifier` grammar element
+ /// @returns the modifier or DerivativeModifier::kNone if none matched
+ ast::DerivativeModifier derivative_modifier();
+ /// Parses the recursive part of the `multiplicative_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> multiplicative_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses the `multiplicative_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> multiplicative_expression();
+ /// Parses the recursive part of the `additive_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> additive_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses the `additive_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> additive_expression();
+ /// Parses the recursive part of the `shift_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> shift_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses the `shift_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> shift_expression();
+ /// Parses the recursive part of the `relational_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> relational_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses the `relational_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> relational_expression();
+ /// Parses the recursive part of the `equality_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> equality_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses the `equality_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> equality_expression();
+ /// Parses the recursive part of the `and_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> and_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses the `and_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> and_expression();
+ /// Parses the recursive part of the `exclusive_or_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> exclusive_or_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses the `exclusive_or_expression` grammar elememnt
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> exclusive_or_expression();
+ /// Parses the recursive part of the `inclusive_or_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> inclusive_or_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses the `inclusive_or_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> inclusive_or_expression();
+ /// Parses the recursive part of the `logical_and_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> logical_and_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses a `logical_and_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> logical_and_expression();
+ /// Parses the recursive part of the `logical_or_expression`
+ /// @param lhs the left side of the expression
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> logical_or_expr(
+ std::unique_ptr<ast::Expression> lhs);
+ /// Parses a `logical_or_expression` grammar element
+ /// @returns the parsed expression or nullptr
+ std::unique_ptr<ast::Expression> logical_or_expression();
+ /// Parses a `assignment_stmt` grammar element
+ /// @returns the parsed assignment or nullptr
+ std::unique_ptr<ast::AssignmentStatement> assignment_stmt();
+
+ private:
+ ast::type::Type* type_decl_pointer(Token t);
+ ast::type::Type* type_decl_vector(Token t);
+ ast::type::Type* type_decl_array(Token t);
+ ast::type::Type* type_decl_matrix(Token t);
+
+ std::string error_;
+ std::unique_ptr<Lexer> lexer_;
+ std::deque<Token> token_queue_;
+ std::unordered_map<std::string, ast::type::Type*> registered_aliases_;
+ ast::Module module_;
+};
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
+
+#endif // SRC_READER_WGSL_PARSER_IMPL_H_
diff --git a/src/reader/wgsl/parser_impl_additive_expression_test.cc b/src/reader/wgsl/parser_impl_additive_expression_test.cc
new file mode 100644
index 0000000..d69d61e
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_additive_expression_test.cc
@@ -0,0 +1,97 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, AdditiveExpression_Parses_Plus) {
+ ParserImpl p{"a + true"};
+ auto e = p.additive_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kAdd, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, AdditiveExpression_Parses_Minus) {
+ ParserImpl p{"a - true"};
+ auto e = p.additive_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kSubtract, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, AdditiveExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} + true"};
+ auto e = p.additive_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, AdditiveExpression_InvalidRHS) {
+ ParserImpl p{"true + if (a) {}"};
+ auto e = p.additive_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse right side of + expression");
+}
+
+TEST_F(ParserImplTest, AdditiveExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.additive_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_and_expression_test.cc b/src/reader/wgsl/parser_impl_and_expression_test.cc
new file mode 100644
index 0000000..be1c581
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_and_expression_test.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, AndExpression_Parses) {
+ ParserImpl p{"a & true"};
+ auto e = p.and_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kAnd, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, AndExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} & true"};
+ auto e = p.and_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, AndExpression_InvalidRHS) {
+ ParserImpl p{"true & if (a) {}"};
+ auto e = p.and_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse right side of & expression");
+}
+
+TEST_F(ParserImplTest, AndExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.and_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_argument_expression_list_test.cc b/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
new file mode 100644
index 0000000..e053532
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_argument_expression_list_test.cc
@@ -0,0 +1,67 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/unary_derivative_expression.h"
+#include "src/ast/unary_method_expression.h"
+#include "src/ast/unary_op_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ArgumentExpressionList_Parses) {
+ ParserImpl p{"a"};
+ auto e = p.argument_expression_list();
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ ASSERT_EQ(e.size(), 1);
+ ASSERT_TRUE(e[0]->IsIdentifier());
+}
+
+TEST_F(ParserImplTest, ArgumentExpressionList_ParsesMultiple) {
+ ParserImpl p{"a, -33, 1+2"};
+ auto e = p.argument_expression_list();
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ ASSERT_EQ(e.size(), 3);
+ ASSERT_TRUE(e[0]->IsIdentifier());
+ ASSERT_TRUE(e[1]->IsInitializer());
+ ASSERT_TRUE(e[2]->IsRelational());
+}
+
+TEST_F(ParserImplTest, ArgumentExpressionList_HandlesMissingExpression) {
+ ParserImpl p{"a, "};
+ auto e = p.argument_expression_list();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:4: unable to parse argument expression after comma");
+}
+
+TEST_F(ParserImplTest, ArgumentExpressionList_HandlesInvalidExpression) {
+ ParserImpl p{"if(a) {}"};
+ auto e = p.argument_expression_list();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:1: unable to parse argument expression");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_assignment_stmt_test.cc b/src/reader/wgsl/parser_impl_assignment_stmt_test.cc
new file mode 100644
index 0000000..9de59f3
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_assignment_stmt_test.cc
@@ -0,0 +1,136 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/assignment_statement.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/literal.h"
+#include "src/ast/member_accessor_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, AssignmentStmt_Parses_ToVariable) {
+ ParserImpl p{"a = 123"};
+ auto e = p.assignment_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsAssign());
+ ASSERT_NE(e->lhs(), nullptr);
+ ASSERT_NE(e->rhs(), nullptr);
+
+ ASSERT_TRUE(e->lhs()->IsIdentifier());
+ auto ident = e->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(e->rhs()->IsInitializer());
+ ASSERT_TRUE(e->rhs()->AsInitializer()->IsConstInitializer());
+
+ auto init = e->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_NE(init->literal(), nullptr);
+ ASSERT_TRUE(init->literal()->IsInt());
+ EXPECT_EQ(init->literal()->AsInt()->value(), 123);
+}
+
+TEST_F(ParserImplTest, AssignmentStmt_Parses_ToMember) {
+ ParserImpl p{"a.b.c[2].d = 123"};
+ auto e = p.assignment_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsAssign());
+ ASSERT_NE(e->lhs(), nullptr);
+ ASSERT_NE(e->rhs(), nullptr);
+
+ ASSERT_TRUE(e->rhs()->IsInitializer());
+ ASSERT_TRUE(e->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = e->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_NE(init->literal(), nullptr);
+ ASSERT_TRUE(init->literal()->IsInt());
+ EXPECT_EQ(init->literal()->AsInt()->value(), 123);
+
+ ASSERT_TRUE(e->lhs()->IsMemberAccessor());
+ auto mem = e->lhs()->AsMemberAccessor();
+
+ ASSERT_TRUE(mem->member()->IsIdentifier());
+ auto ident = mem->member()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "d");
+
+ ASSERT_TRUE(mem->structure()->IsArrayAccessor());
+ auto ary = mem->structure()->AsArrayAccessor();
+
+ ASSERT_TRUE(ary->idx_expr()->IsInitializer());
+ ASSERT_TRUE(ary->idx_expr()->AsInitializer()->IsConstInitializer());
+ init = ary->idx_expr()->AsInitializer()->AsConstInitializer();
+ ASSERT_NE(init->literal(), nullptr);
+ ASSERT_TRUE(init->literal()->IsInt());
+ EXPECT_EQ(init->literal()->AsInt()->value(), 2);
+
+ ASSERT_TRUE(ary->array()->IsMemberAccessor());
+ mem = ary->array()->AsMemberAccessor();
+ ASSERT_TRUE(mem->member()->IsIdentifier());
+ ident = mem->member()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "c");
+
+ ASSERT_TRUE(mem->structure()->IsMemberAccessor());
+ mem = mem->structure()->AsMemberAccessor();
+
+ ASSERT_TRUE(mem->structure()->IsIdentifier());
+ ident = mem->structure()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(mem->member()->IsIdentifier());
+ ident = mem->member()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "b");
+}
+
+TEST_F(ParserImplTest, AssignmentStmt_MissingEqual) {
+ ParserImpl p{"a.b.c[2].d 123"};
+ auto e = p.assignment_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: missing = for assignment");
+}
+
+TEST_F(ParserImplTest, AssignmentStmt_InvalidLHS) {
+ ParserImpl p{"if (true) {} = 123"};
+ auto e = p.assignment_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, AssignmentStmt_InvalidRHS) {
+ ParserImpl p{"a.b.c[2].d = if (true) {}"};
+ auto e = p.assignment_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: unable to parse right side of assignment");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_body_stmt_test.cc b/src/reader/wgsl/parser_impl_body_stmt_test.cc
new file mode 100644
index 0000000..6f57a60
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_body_stmt_test.cc
@@ -0,0 +1,61 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, BodyStmt) {
+ ParserImpl p{R"({
+ kill;
+ nop;
+ return 1 + b / 2;
+})"};
+ auto e = p.body_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 3);
+ EXPECT_TRUE(e[0]->IsKill());
+ EXPECT_TRUE(e[1]->IsNop());
+ EXPECT_TRUE(e[2]->IsReturn());
+}
+
+TEST_F(ParserImplTest, BodyStmt_Empty) {
+ ParserImpl p{"{}"};
+ auto e = p.body_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ EXPECT_EQ(e.size(), 0);
+}
+
+TEST_F(ParserImplTest, BodyStmt_InvalidStmt) {
+ ParserImpl p{"{fn main() -> void {}}"};
+ auto e = p.body_stmt();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:2: missing }");
+}
+
+TEST_F(ParserImplTest, BodyStmt_MissingRightParen) {
+ ParserImpl p{"{return;"};
+ auto e = p.body_stmt();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:9: missing }");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_break_stmt_test.cc b/src/reader/wgsl/parser_impl_break_stmt_test.cc
new file mode 100644
index 0000000..932871e
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_break_stmt_test.cc
@@ -0,0 +1,77 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/break_statement.h"
+#include "src/ast/return_statement.h"
+#include "src/ast/statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, BreakStmt) {
+ ParserImpl p{"break"};
+ auto e = p.break_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsBreak());
+ EXPECT_EQ(e->condition(), ast::StatementCondition::kNone);
+ EXPECT_EQ(e->conditional(), nullptr);
+}
+
+TEST_F(ParserImplTest, BreakStmt_WithIf) {
+ ParserImpl p{"break if (a == b)"};
+ auto e = p.break_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsBreak());
+ EXPECT_EQ(e->condition(), ast::StatementCondition::kIf);
+ ASSERT_NE(e->conditional(), nullptr);
+ EXPECT_TRUE(e->conditional()->IsRelational());
+}
+
+TEST_F(ParserImplTest, BreakStmt_WithUnless) {
+ ParserImpl p{"break unless (a == b)"};
+ auto e = p.break_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsBreak());
+ EXPECT_EQ(e->condition(), ast::StatementCondition::kUnless);
+ ASSERT_NE(e->conditional(), nullptr);
+ EXPECT_TRUE(e->conditional()->IsRelational());
+}
+
+TEST_F(ParserImplTest, BreakStmt_InvalidRHS) {
+ ParserImpl p{"break if (a = b)"};
+ auto e = p.break_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: expected )");
+}
+
+TEST_F(ParserImplTest, BreakStmt_MissingRHS) {
+ ParserImpl p{"break if"};
+ auto e = p.break_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: expected (");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_builtin_decoration_test.cc b/src/reader/wgsl/parser_impl_builtin_decoration_test.cc
new file mode 100644
index 0000000..4b5edf1
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_builtin_decoration_test.cc
@@ -0,0 +1,74 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/builtin.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+struct BuiltinData {
+ const char* input;
+ ast::Builtin result;
+};
+inline std::ostream& operator<<(std::ostream& out, BuiltinData data) {
+ out << std::string(data.input);
+ return out;
+}
+using BuiltinTest = testing::TestWithParam<BuiltinData>;
+TEST_P(BuiltinTest, Parses) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+
+ auto builtin = p.builtin_decoration();
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(builtin, params.result);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsEof());
+}
+INSTANTIATE_TEST_SUITE_P(
+ ParserImplTest,
+ BuiltinTest,
+ testing::Values(
+ BuiltinData{"position", ast::Builtin::kPosition},
+ BuiltinData{"vertex_idx", ast::Builtin::kVertexIdx},
+ BuiltinData{"instance_idx", ast::Builtin::kInstanceIdx},
+ BuiltinData{"front_facing", ast::Builtin::kFrontFacing},
+ BuiltinData{"frag_coord", ast::Builtin::kFragCoord},
+ BuiltinData{"frag_depth", ast::Builtin::kFragDepth},
+ BuiltinData{"num_workgroups", ast::Builtin::kNumWorkgroups},
+ BuiltinData{"workgroup_size", ast::Builtin::kWorkgroupSize},
+ BuiltinData{"local_invocation_id", ast::Builtin::kLocalInvocationId},
+ BuiltinData{"local_invocation_idx", ast::Builtin::kLocalInvocationIdx},
+ BuiltinData{"global_invocation_id",
+ ast::Builtin::kGlobalInvocationId}));
+
+TEST_F(ParserImplTest, BuiltinDecoration_NoMatch) {
+ ParserImpl p{"not-a-builtin"};
+ auto builtin = p.builtin_decoration();
+ ASSERT_EQ(builtin, ast::Builtin::kNone);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "not");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_case_body_test.cc b/src/reader/wgsl/parser_impl_case_body_test.cc
new file mode 100644
index 0000000..621a8a4
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_case_body_test.cc
@@ -0,0 +1,69 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, CaseBody_Empty) {
+ ParserImpl p{""};
+ auto e = p.case_body();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ EXPECT_EQ(e.size(), 0);
+}
+
+TEST_F(ParserImplTest, CaseBody_Statements) {
+ ParserImpl p{R"(
+ var a: i32;
+ a = 2;)"};
+
+ auto e = p.case_body();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 2);
+ EXPECT_TRUE(e[0]->IsVariable());
+ EXPECT_TRUE(e[1]->IsAssign());
+}
+
+TEST_F(ParserImplTest, CaseBody_InvalidStatement) {
+ ParserImpl p{"a ="};
+ auto e = p.case_body();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(e.size(), 0);
+ EXPECT_EQ(p.error(), "1:4: unable to parse right side of assignment");
+}
+
+TEST_F(ParserImplTest, CaseBody_Fallthrough) {
+ ParserImpl p{"fallthrough;"};
+ auto e = p.case_body();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 1);
+ EXPECT_TRUE(e[0]->IsFallthrough());
+}
+
+TEST_F(ParserImplTest, CaseBody_Fallthrough_MissingSemicolon) {
+ ParserImpl p{"fallthrough"};
+ auto e = p.case_body();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(e.size(), 0);
+ EXPECT_EQ(p.error(), "1:12: missing ;");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_const_expr_test.cc b/src/reader/wgsl/parser_impl_const_expr_test.cc
new file mode 100644
index 0000000..ffdb348
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_const_expr_test.cc
@@ -0,0 +1,127 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/float_literal.h"
+#include "src/ast/type/vector_type.h"
+#include "src/ast/type_initializer_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ConstExpr_TypeDecl) {
+ ParserImpl p{"vec2<f32>(1., 2.)"};
+ auto e = p.const_expr();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsInitializer());
+ ASSERT_TRUE(e->AsInitializer()->IsTypeInitializer());
+
+ auto t = e->AsInitializer()->AsTypeInitializer();
+ ASSERT_TRUE(t->type()->IsVector());
+ EXPECT_EQ(t->type()->AsVector()->size(), 2);
+
+ ASSERT_EQ(t->values().size(), 2);
+ auto& v = t->values();
+
+ ASSERT_TRUE(v[0]->IsInitializer());
+ ASSERT_TRUE(v[0]->AsInitializer()->IsConstInitializer());
+ auto c = v[0]->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(c->literal()->IsFloat());
+ EXPECT_FLOAT_EQ(c->literal()->AsFloat()->value(), 1.);
+
+ ASSERT_TRUE(v[1]->IsInitializer());
+ ASSERT_TRUE(v[1]->AsInitializer()->IsConstInitializer());
+ c = v[1]->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(c->literal()->IsFloat());
+ EXPECT_FLOAT_EQ(c->literal()->AsFloat()->value(), 2.);
+}
+
+TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingRightParen) {
+ ParserImpl p{"vec2<f32>(1., 2."};
+ auto e = p.const_expr();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:17: missing ) for type initializer");
+}
+
+TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingLeftParen) {
+ ParserImpl p{"vec2<f32> 1., 2.)"};
+ auto e = p.const_expr();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing ( for type initializer");
+}
+
+TEST_F(ParserImplTest, ConstExpr_TypeDecl_HangingComma) {
+ ParserImpl p{"vec2<f32>(1.,)"};
+ auto e = p.const_expr();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: unable to parse const literal");
+}
+
+TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingComma) {
+ ParserImpl p{"vec2<f32>(1. 2."};
+ auto e = p.const_expr();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: missing ) for type initializer");
+}
+
+TEST_F(ParserImplTest, ConstExpr_MissingExpr) {
+ ParserImpl p{"vec2<f32>()"};
+ auto e = p.const_expr();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: unable to parse const literal");
+}
+
+TEST_F(ParserImplTest, ConstExpr_InvalidExpr) {
+ ParserImpl p{"vec2<f32>(1., if(a) {})"};
+ auto e = p.const_expr();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: unable to parse const literal");
+}
+
+TEST_F(ParserImplTest, ConstExpr_ConstLiteral) {
+ ParserImpl p{"true"};
+ auto e = p.const_expr();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsInitializer());
+ ASSERT_TRUE(e->AsInitializer()->IsConstInitializer());
+ auto c = e->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(c->literal()->IsBool());
+ EXPECT_TRUE(c->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, ConstExpr_ConstLiteral_Invalid) {
+ ParserImpl p{"invalid"};
+ auto e = p.const_expr();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:1: unknown type alias 'invalid'");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_const_literal_test.cc b/src/reader/wgsl/parser_impl_const_literal_test.cc
new file mode 100644
index 0000000..f9572ea
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_const_literal_test.cc
@@ -0,0 +1,88 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/float_literal.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/uint_literal.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ConstLiteral_Int) {
+ ParserImpl p{"-234"};
+ auto c = p.const_literal();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(c, nullptr);
+ ASSERT_TRUE(c->IsInt());
+ EXPECT_EQ(c->AsInt()->value(), -234);
+}
+
+TEST_F(ParserImplTest, ConstLiteral_Uint) {
+ ParserImpl p{"234u"};
+ auto c = p.const_literal();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(c, nullptr);
+ ASSERT_TRUE(c->IsUint());
+ EXPECT_EQ(c->AsUint()->value(), 234u);
+}
+
+TEST_F(ParserImplTest, ConstLiteral_Float) {
+ ParserImpl p{"234.e12"};
+ auto c = p.const_literal();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(c, nullptr);
+ ASSERT_TRUE(c->IsFloat());
+ EXPECT_FLOAT_EQ(c->AsFloat()->value(), 234e12);
+}
+
+TEST_F(ParserImplTest, ConstLiteral_InvalidFloat) {
+ ParserImpl p{"1.2e+256"};
+ auto c = p.const_literal();
+ ASSERT_EQ(c, nullptr);
+}
+
+TEST_F(ParserImplTest, ConstLiteral_True) {
+ ParserImpl p{"true"};
+ auto c = p.const_literal();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(c, nullptr);
+ ASSERT_TRUE(c->IsBool());
+ EXPECT_TRUE(c->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, ConstLiteral_False) {
+ ParserImpl p{"false"};
+ auto c = p.const_literal();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(c, nullptr);
+ ASSERT_TRUE(c->IsBool());
+ EXPECT_TRUE(c->AsBool()->IsFalse());
+}
+
+TEST_F(ParserImplTest, ConstLiteral_NoMatch) {
+ ParserImpl p{"another-token"};
+ auto c = p.const_literal();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_EQ(c, nullptr);
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_continue_stmt_test.cc b/src/reader/wgsl/parser_impl_continue_stmt_test.cc
new file mode 100644
index 0000000..335cdf2
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_continue_stmt_test.cc
@@ -0,0 +1,77 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/continue_statement.h"
+#include "src/ast/return_statement.h"
+#include "src/ast/statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ContinueStmt) {
+ ParserImpl p{"continue"};
+ auto e = p.continue_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsContinue());
+ EXPECT_EQ(e->condition(), ast::StatementCondition::kNone);
+ EXPECT_EQ(e->conditional(), nullptr);
+}
+
+TEST_F(ParserImplTest, ContinueStmt_WithIf) {
+ ParserImpl p{"continue if (a == b)"};
+ auto e = p.continue_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsContinue());
+ EXPECT_EQ(e->condition(), ast::StatementCondition::kIf);
+ ASSERT_NE(e->conditional(), nullptr);
+ EXPECT_TRUE(e->conditional()->IsRelational());
+}
+
+TEST_F(ParserImplTest, ContinueStmt_WithUnless) {
+ ParserImpl p{"continue unless (a == b)"};
+ auto e = p.continue_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsContinue());
+ EXPECT_EQ(e->condition(), ast::StatementCondition::kUnless);
+ ASSERT_NE(e->conditional(), nullptr);
+ EXPECT_TRUE(e->conditional()->IsRelational());
+}
+
+TEST_F(ParserImplTest, ContinueStmt_InvalidRHS) {
+ ParserImpl p{"continue if (a = b)"};
+ auto e = p.continue_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:16: expected )");
+}
+
+TEST_F(ParserImplTest, ContinueStmt_MissingRHS) {
+ ParserImpl p{"continue if"};
+ auto e = p.continue_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: expected (");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_continuing_stmt_test.cc b/src/reader/wgsl/parser_impl_continuing_stmt_test.cc
new file mode 100644
index 0000000..93b79c0
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_continuing_stmt_test.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ContinuingStmt) {
+ ParserImpl p{"continuing { nop; }"};
+ auto e = p.continuing_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 1);
+ ASSERT_TRUE(e[0]->IsNop());
+}
+
+TEST_F(ParserImplTest, ContinuingStmt_InvalidBody) {
+ ParserImpl p{"continuing { nop }"};
+ auto e = p.continuing_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e.size(), 0);
+ EXPECT_EQ(p.error(), "1:18: missing ;");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_derivative_modifier_test.cc b/src/reader/wgsl/parser_impl_derivative_modifier_test.cc
new file mode 100644
index 0000000..4c471d2
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_derivative_modifier_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/derivative_modifier.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+struct DerivativeModifierData {
+ const char* input;
+ ast::DerivativeModifier result;
+};
+inline std::ostream& operator<<(std::ostream& out,
+ DerivativeModifierData data) {
+ out << std::string(data.input);
+ return out;
+}
+using DerivativeModifierTest = testing::TestWithParam<DerivativeModifierData>;
+TEST_P(DerivativeModifierTest, Parses) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+
+ auto mod = p.derivative_modifier();
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(mod, params.result);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsEof());
+}
+INSTANTIATE_TEST_SUITE_P(
+ ParserImplTest,
+ DerivativeModifierTest,
+ testing::Values(
+ DerivativeModifierData{"fine", ast::DerivativeModifier::kFine},
+ DerivativeModifierData{"coarse", ast::DerivativeModifier::kCoarse}));
+
+TEST_F(ParserImplTest, DerivativeModifier_NoMatch) {
+ ParserImpl p{"not-a-modifier"};
+ auto stage = p.derivative_modifier();
+ ASSERT_EQ(stage, ast::DerivativeModifier::kNone);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "not");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_else_stmt_test.cc b/src/reader/wgsl/parser_impl_else_stmt_test.cc
new file mode 100644
index 0000000..cbfeff6
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_else_stmt_test.cc
@@ -0,0 +1,53 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/else_statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ElseStmt) {
+ ParserImpl p{"else { a = b; c = d; }"};
+ auto e = p.else_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsElse());
+ ASSERT_EQ(e->condition(), nullptr);
+ EXPECT_EQ(e->body().size(), 2);
+}
+
+TEST_F(ParserImplTest, ElseStmt_InvalidBody) {
+ ParserImpl p{"else { fn main() -> void {}}"};
+ auto e = p.else_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing }");
+}
+
+TEST_F(ParserImplTest, ElseStmt_MissingBody) {
+ ParserImpl p{"else"};
+ auto e = p.else_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing {");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_elseif_stmt_test.cc b/src/reader/wgsl/parser_impl_elseif_stmt_test.cc
new file mode 100644
index 0000000..b597fed
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_elseif_stmt_test.cc
@@ -0,0 +1,70 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/else_statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ElseIfStmt) {
+ ParserImpl p{"elseif (a == 4) { a = b; c = d; }"};
+ auto e = p.elseif_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 1);
+
+ ASSERT_TRUE(e[0]->IsElse());
+ ASSERT_NE(e[0]->condition(), nullptr);
+ ASSERT_TRUE(e[0]->condition()->IsRelational());
+ EXPECT_EQ(e[0]->body().size(), 2);
+}
+
+TEST_F(ParserImplTest, ElseIfStmt_Multiple) {
+ ParserImpl p{"elseif (a == 4) { a = b; c = d; } elseif(c) { d = 2; }"};
+ auto e = p.elseif_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 2);
+
+ ASSERT_TRUE(e[0]->IsElse());
+ ASSERT_NE(e[0]->condition(), nullptr);
+ ASSERT_TRUE(e[0]->condition()->IsRelational());
+ EXPECT_EQ(e[0]->body().size(), 2);
+
+ ASSERT_TRUE(e[1]->IsElse());
+ ASSERT_NE(e[1]->condition(), nullptr);
+ ASSERT_TRUE(e[1]->condition()->IsIdentifier());
+ EXPECT_EQ(e[1]->body().size(), 1);
+}
+
+TEST_F(ParserImplTest, ElseIfStmt_InvalidBody) {
+ ParserImpl p{"elseif (true) { fn main() -> void {}}"};
+ auto e = p.elseif_stmt();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:17: missing }");
+}
+
+TEST_F(ParserImplTest, ElseIfStmt_MissingBody) {
+ ParserImpl p{"elseif (true)"};
+ auto e = p.elseif_stmt();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:14: missing {");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_entry_point_decl_test.cc b/src/reader/wgsl/parser_impl_entry_point_decl_test.cc
new file mode 100644
index 0000000..3afdc90
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_entry_point_decl_test.cc
@@ -0,0 +1,121 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/variable.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, EntryPoint_Parses) {
+ ParserImpl p{"entry_point fragment = main"};
+ auto e = p.entry_point_decl();
+ ASSERT_NE(e, nullptr);
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(e->stage(), ast::PipelineStage::kFragment);
+ EXPECT_EQ(e->name(), "main");
+ EXPECT_EQ(e->function_name(), "main");
+}
+
+TEST_F(ParserImplTest, EntryPoint_ParsesWithStringName) {
+ ParserImpl p{R"(entry_point vertex as "main" = vtx_main)"};
+ auto e = p.entry_point_decl();
+ ASSERT_NE(e, nullptr);
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(e->stage(), ast::PipelineStage::kVertex);
+ EXPECT_EQ(e->name(), "main");
+ EXPECT_EQ(e->function_name(), "vtx_main");
+}
+
+TEST_F(ParserImplTest, EntryPoint_ParsesWithIdentName) {
+ ParserImpl p{R"(entry_point vertex as main = vtx_main)"};
+ auto e = p.entry_point_decl();
+ ASSERT_NE(e, nullptr);
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(e->stage(), ast::PipelineStage::kVertex);
+ EXPECT_EQ(e->name(), "main");
+ EXPECT_EQ(e->function_name(), "vtx_main");
+}
+
+TEST_F(ParserImplTest, EntryPoint_MissingFnName) {
+ ParserImpl p{R"(entry_point vertex as main =)"};
+ auto e = p.entry_point_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:29: invalid function name for entry point");
+}
+
+TEST_F(ParserImplTest, EntryPoint_InvalidFnName) {
+ ParserImpl p{R"(entry_point vertex as main = 123)"};
+ auto e = p.entry_point_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:30: invalid function name for entry point");
+}
+
+TEST_F(ParserImplTest, EntryPoint_MissingEqual) {
+ ParserImpl p{R"(entry_point vertex as main vtx_main)"};
+ auto e = p.entry_point_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:28: missing = for entry point");
+}
+
+TEST_F(ParserImplTest, EntryPoint_MissingName) {
+ ParserImpl p{R"(entry_point vertex as = vtx_main)"};
+ auto e = p.entry_point_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:23: invalid name for entry point");
+}
+
+TEST_F(ParserImplTest, EntryPoint_InvalidName) {
+ ParserImpl p{R"(entry_point vertex as 123 = vtx_main)"};
+ auto e = p.entry_point_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:23: invalid name for entry point");
+}
+
+TEST_F(ParserImplTest, EntryPoint_MissingStageWithIdent) {
+ ParserImpl p{R"(entry_point as 123 = vtx_main)"};
+ auto e = p.entry_point_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: missing pipeline stage for entry point");
+}
+
+TEST_F(ParserImplTest, EntryPoint_MissingStage) {
+ ParserImpl p{R"(entry_point = vtx_main)"};
+ auto e = p.entry_point_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: missing pipeline stage for entry point");
+}
+
+TEST_F(ParserImplTest, EntryPoint_InvalidStage) {
+ ParserImpl p{R"(entry_point invalid = vtx_main)"};
+ auto e = p.entry_point_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: missing pipeline stage for entry point");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_equality_expression_test.cc b/src/reader/wgsl/parser_impl_equality_expression_test.cc
new file mode 100644
index 0000000..73a3922
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_equality_expression_test.cc
@@ -0,0 +1,97 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, EqualityExpression_Parses_Equal) {
+ ParserImpl p{"a == true"};
+ auto e = p.equality_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kEqual, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, EqualityExpression_Parses_NotEqual) {
+ ParserImpl p{"a != true"};
+ auto e = p.equality_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kNotEqual, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, EqualityExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} == true"};
+ auto e = p.equality_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, EqualityExpression_InvalidRHS) {
+ ParserImpl p{"true == if (a) {}"};
+ auto e = p.equality_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: unable to parse right side of == expression");
+}
+
+TEST_F(ParserImplTest, EqualityExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.equality_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_exclusive_or_expression_test.cc b/src/reader/wgsl/parser_impl_exclusive_or_expression_test.cc
new file mode 100644
index 0000000..dfc7af9
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_exclusive_or_expression_test.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ExclusiveOrExpression_Parses) {
+ ParserImpl p{"a ^ true"};
+ auto e = p.exclusive_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kXor, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, ExclusiveOrExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} ^ true"};
+ auto e = p.exclusive_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, ExclusiveOrExpression_InvalidRHS) {
+ ParserImpl p{"true ^ if (a) {}"};
+ auto e = p.exclusive_or_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse right side of ^ expression");
+}
+
+TEST_F(ParserImplTest, ExclusiveOrExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.exclusive_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_function_decl_test.cc b/src/reader/wgsl/parser_impl_function_decl_test.cc
new file mode 100644
index 0000000..869e3d5
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_function_decl_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/function.h"
+#include "src/ast/type/type.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, FunctionDecl) {
+ ParserImpl p{"fn main(a : i32, b : f32) -> void { return; }"};
+ auto f = p.function_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(f, nullptr);
+
+ EXPECT_EQ(f->name(), "main");
+ ASSERT_NE(f->return_type(), nullptr);
+ EXPECT_TRUE(f->return_type()->IsVoid());
+
+ ASSERT_EQ(f->params().size(), 2);
+ EXPECT_EQ(f->params()[0]->name(), "a");
+ EXPECT_EQ(f->params()[1]->name(), "b");
+
+ ASSERT_NE(f->return_type(), nullptr);
+ EXPECT_TRUE(f->return_type()->IsVoid());
+
+ ASSERT_EQ(f->body().size(), 1);
+ EXPECT_TRUE(f->body()[0]->IsReturn());
+}
+
+TEST_F(ParserImplTest, FunctionDecl_InvalidHeader) {
+ ParserImpl p{"fn main() -> { }"};
+ auto f = p.function_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:14: unable to determine function return type");
+}
+
+TEST_F(ParserImplTest, FunctionDecl_InvalidBody) {
+ ParserImpl p{"fn main() -> void { return }"};
+ auto f = p.function_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:28: missing ;");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_function_header_test.cc b/src/reader/wgsl/parser_impl_function_header_test.cc
new file mode 100644
index 0000000..1ac9bc6
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_function_header_test.cc
@@ -0,0 +1,105 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/function.h"
+#include "src/ast/type/type.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, FunctionHeader) {
+ ParserImpl p{"fn main(a : i32, b: f32) -> void"};
+ auto f = p.function_header();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(f, nullptr);
+
+ EXPECT_EQ(f->name(), "main");
+ ASSERT_EQ(f->params().size(), 2);
+ EXPECT_EQ(f->params()[0]->name(), "a");
+ EXPECT_EQ(f->params()[1]->name(), "b");
+ EXPECT_TRUE(f->return_type()->IsVoid());
+}
+
+TEST_F(ParserImplTest, FunctionHeader_MissingIdent) {
+ ParserImpl p{"fn () ->"};
+ auto f = p.function_header();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:4: missing identifier for function");
+}
+
+TEST_F(ParserImplTest, FunctionHeader_InvalidIdent) {
+ ParserImpl p{"fn 133main() -> i32"};
+ auto f = p.function_header();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:4: missing identifier for function");
+}
+
+TEST_F(ParserImplTest, FunctionHeader_MissingParenLeft) {
+ ParserImpl p{"fn main) -> i32"};
+ auto f = p.function_header();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing ( for function declaration");
+}
+
+TEST_F(ParserImplTest, FunctionHeader_InvalidParamList) {
+ ParserImpl p{"fn main(a :i32,) -> i32"};
+ auto f = p.function_header();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:15: found , but no variable declaration");
+}
+
+TEST_F(ParserImplTest, FunctionHeader_MissingParenRight) {
+ ParserImpl p{"fn main( -> i32"};
+ auto f = p.function_header();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:10: missing ) for function declaration");
+}
+
+TEST_F(ParserImplTest, FunctionHeader_MissingArrow) {
+ ParserImpl p{"fn main() i32"};
+ auto f = p.function_header();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing -> for function declaration");
+}
+
+TEST_F(ParserImplTest, FunctionHeader_InvalidReturnType) {
+ ParserImpl p{"fn main() -> invalid"};
+ auto f = p.function_header();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:14: unknown type alias 'invalid'");
+}
+
+TEST_F(ParserImplTest, FunctionHeader_MissingReturnType) {
+ ParserImpl p{"fn main() ->"};
+ auto f = p.function_header();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(f, nullptr);
+ EXPECT_EQ(p.error(), "1:13: unable to determine function return type");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_function_type_decl_test.cc b/src/reader/wgsl/parser_impl_function_type_decl_test.cc
new file mode 100644
index 0000000..59e17bc
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_function_type_decl_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2020 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 <memory>
+
+#include "gtest/gtest.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/vector_type.h"
+#include "src/ast/type/void_type.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, FunctionTypeDecl_Void) {
+ auto tm = TypeManager::Instance();
+ auto v = tm->Get(std::make_unique<ast::type::VoidType>());
+
+ ParserImpl p{"void"};
+ auto e = p.function_type_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, v);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, FunctionTypeDecl_Type) {
+ auto tm = TypeManager::Instance();
+ auto f32 = tm->Get(std::make_unique<ast::type::F32Type>());
+ auto vec2 = tm->Get(std::make_unique<ast::type::VectorType>(f32, 2));
+
+ ParserImpl p{"vec2<f32>"};
+ auto e = p.function_type_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, vec2);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, FunctionTypeDecl_InvalidType) {
+ ParserImpl p{"vec2<invalid>"};
+ auto e = p.function_type_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: unknown type alias 'invalid'");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_global_constant_decl_test.cc b/src/reader/wgsl/parser_impl_global_constant_decl_test.cc
new file mode 100644
index 0000000..5f9768b
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_global_constant_decl_test.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/decorated_variable.h"
+#include "src/ast/variable_decoration.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, GlobalConstantDecl) {
+ ParserImpl p{"const a : f32 = 1."};
+ auto e = p.global_constant_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ EXPECT_TRUE(e->is_const());
+ EXPECT_EQ(e->name(), "a");
+ ASSERT_NE(e->type(), nullptr);
+ EXPECT_TRUE(e->type()->IsF32());
+
+ ASSERT_NE(e->initializer(), nullptr);
+ EXPECT_TRUE(e->initializer()->IsInitializer());
+}
+
+TEST_F(ParserImplTest, GlobalConstantDecl_MissingEqual) {
+ ParserImpl p{"const a: f32 1."};
+ auto e = p.global_constant_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: missing = for const declaration");
+}
+
+TEST_F(ParserImplTest, GlobalConstantDecl_InvalidVariable) {
+ ParserImpl p{"const a: invalid = 1."};
+ auto e = p.global_constant_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:10: unknown type alias 'invalid'");
+}
+
+TEST_F(ParserImplTest, GlobalConstantDecl_InvalidExpression) {
+ ParserImpl p{"const a: f32 = if (a) {}"};
+ auto e = p.global_constant_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:16: unable to parse const literal");
+}
+
+TEST_F(ParserImplTest, GlobalConstantDecl_MissingExpression) {
+ ParserImpl p{"const a: f32 ="};
+ auto e = p.global_constant_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: unable to parse const literal");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_global_decl_test.cc b/src/reader/wgsl/parser_impl_global_decl_test.cc
new file mode 100644
index 0000000..86c22a7
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_global_decl_test.cc
@@ -0,0 +1,178 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, GlobalDecl_Semicolon) {
+ ParserImpl p(";");
+ p.global_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+}
+
+TEST_F(ParserImplTest, GlobalDecl_Import) {
+ ParserImpl p{R"(import "GLSL.std.430" as glsl;)"};
+ p.global_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ auto m = p.module();
+ ASSERT_EQ(1, m.imports().size());
+
+ const auto& import = m.imports()[0];
+ EXPECT_EQ("GLSL.std.430", import->path());
+ EXPECT_EQ("glsl", import->name());
+}
+
+TEST_F(ParserImplTest, GlobalDecl_Import_Invalid) {
+ ParserImpl p{R"(import as glsl;)"};
+ p.global_decl();
+
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:8: missing path for import");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_Import_Invalid_MissingSemicolon) {
+ ParserImpl p{R"(import "GLSL.std.430" as glsl)"};
+ p.global_decl();
+
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:30: missing ';' for import");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalVariable) {
+ ParserImpl p{"var<out> a : vec2<i32> = vec2<i32>(1, 2);"};
+ p.global_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ auto m = p.module();
+ ASSERT_EQ(m.global_variables().size(), 1);
+
+ auto v = m.global_variables()[0].get();
+ EXPECT_EQ(v->name(), "a");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_Invalid) {
+ ParserImpl p{"var<out> a : vec2<invalid>;"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:19: unknown type alias 'invalid'");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_MissingSemicolon) {
+ ParserImpl p{"var<out> a : vec2<i32>"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:23: missing ';' for variable declaration");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConstant) {
+ ParserImpl p{"const a : i32 = 2;"};
+ p.global_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ auto m = p.module();
+ ASSERT_EQ(m.global_variables().size(), 1);
+
+ auto v = m.global_variables()[0].get();
+ EXPECT_EQ(v->name(), "a");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_Invalid) {
+ ParserImpl p{"const a : vec2<i32>;"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:20: missing = for const declaration");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingSemicolon) {
+ ParserImpl p{"const a : vec2<i32> = vec2<i32>(1, 2)"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:38: missing ';' for constant declaration");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_EntryPoint) {
+ ParserImpl p{"entry_point vertex = main;"};
+ p.global_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ auto m = p.module();
+ ASSERT_EQ(m.entry_points().size(), 1);
+ EXPECT_EQ(m.entry_points()[0]->name(), "main");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_EntryPoint_Invalid) {
+ ParserImpl p{"entry_point main;"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:13: missing pipeline stage for entry point");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_EntryPoint_MissingSemicolon) {
+ ParserImpl p{"entry_point vertex = main"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:26: missing ';' for entry point");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
+ ParserImpl p{"type A = i32;"};
+ p.global_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ auto m = p.module();
+ ASSERT_EQ(m.alias_types().size(), 1);
+ EXPECT_EQ(m.alias_types()[0]->name(), "A");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_TypeAlias_Invalid) {
+ ParserImpl p{"type A = invalid;"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:10: unknown type alias 'invalid'");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_TypeAlias_MissingSemicolon) {
+ ParserImpl p{"type A = i32"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:13: missing ';' for type alias");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_Function) {
+ ParserImpl p{"fn main() -> void { return; }"};
+ p.global_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ auto m = p.module();
+ ASSERT_EQ(m.functions().size(), 1);
+ EXPECT_EQ(m.functions()[0]->name(), "main");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_Function_Invalid) {
+ ParserImpl p{"fn main() -> { return; }"};
+ p.global_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:14: unable to determine function return type");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_global_variable_decl_test.cc b/src/reader/wgsl/parser_impl_global_variable_decl_test.cc
new file mode 100644
index 0000000..528135c
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_global_variable_decl_test.cc
@@ -0,0 +1,106 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/decorated_variable.h"
+#include "src/ast/variable_decoration.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, GlobalVariableDecl_WithoutInitializer) {
+ ParserImpl p{"var<out> a : f32"};
+ auto e = p.global_variable_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ EXPECT_EQ(e->name(), "a");
+ EXPECT_TRUE(e->type()->IsF32());
+ EXPECT_EQ(e->storage_class(), ast::StorageClass::kOutput);
+
+ ASSERT_EQ(e->initializer(), nullptr);
+ ASSERT_FALSE(e->IsDecorated());
+}
+
+TEST_F(ParserImplTest, GlobalVariableDecl_WithInitializer) {
+ ParserImpl p{"var<out> a : f32 = 1."};
+ auto e = p.global_variable_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ EXPECT_EQ(e->name(), "a");
+ EXPECT_TRUE(e->type()->IsF32());
+ EXPECT_EQ(e->storage_class(), ast::StorageClass::kOutput);
+
+ ASSERT_NE(e->initializer(), nullptr);
+ ASSERT_TRUE(e->initializer()->IsInitializer());
+ ASSERT_TRUE(e->initializer()->AsInitializer()->IsConstInitializer());
+
+ ASSERT_FALSE(e->IsDecorated());
+}
+
+TEST_F(ParserImplTest, GlobalVariableDecl_WithDecoration) {
+ ParserImpl p{"[[binding 2, set 1]] var<out> a : f32"};
+ auto e = p.global_variable_decl();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsDecorated());
+
+ EXPECT_EQ(e->name(), "a");
+ ASSERT_NE(e->type(), nullptr);
+ EXPECT_TRUE(e->type()->IsF32());
+ EXPECT_EQ(e->storage_class(), ast::StorageClass::kOutput);
+
+ ASSERT_EQ(e->initializer(), nullptr);
+
+ ASSERT_TRUE(e->IsDecorated());
+ auto v = e->AsDecorated();
+
+ auto& decos = v->decorations();
+ ASSERT_EQ(decos.size(), 2);
+ ASSERT_TRUE(decos[0]->IsBinding());
+ ASSERT_TRUE(decos[1]->IsSet());
+}
+
+TEST_F(ParserImplTest, GlobalVariableDecl_InvalidDecoration) {
+ ParserImpl p{"[[binding]] var<out> a : f32"};
+ auto e = p.global_variable_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:10: invalid value for binding decoration");
+}
+
+TEST_F(ParserImplTest, GlobalVariableDecl_InvalidConstExpr) {
+ ParserImpl p{"var<out> a : f32 = if (a) {}"};
+ auto e = p.global_variable_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:20: unable to parse const literal");
+}
+
+TEST_F(ParserImplTest, GlobalVariableDecl_InvalidVariableDecl) {
+ ParserImpl p{"var<invalid> a : f32;"};
+ auto e = p.global_variable_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: invalid storage class for variable decoration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_if_stmt_test.cc b/src/reader/wgsl/parser_impl_if_stmt_test.cc
new file mode 100644
index 0000000..32fa433
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_if_stmt_test.cc
@@ -0,0 +1,143 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/else_statement.h"
+#include "src/ast/if_statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, IfStmt) {
+ ParserImpl p{"if (a == 4) { a = b; c = d; }"};
+ auto e = p.if_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsIf());
+ ASSERT_NE(e->condition(), nullptr);
+ ASSERT_TRUE(e->condition()->IsRelational());
+ EXPECT_EQ(e->body().size(), 2);
+ EXPECT_EQ(e->else_statements().size(), 0);
+ EXPECT_EQ(e->premerge().size(), 0);
+}
+
+TEST_F(ParserImplTest, IfStmt_WithElse) {
+ ParserImpl p{"if (a == 4) { a = b; c = d; } elseif(c) { d = 2; } else {}"};
+ auto e = p.if_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsIf());
+ ASSERT_NE(e->condition(), nullptr);
+ ASSERT_TRUE(e->condition()->IsRelational());
+ EXPECT_EQ(e->body().size(), 2);
+
+ ASSERT_EQ(e->else_statements().size(), 2);
+ ASSERT_NE(e->else_statements()[0]->condition(), nullptr);
+ ASSERT_TRUE(e->else_statements()[0]->condition()->IsIdentifier());
+ EXPECT_EQ(e->else_statements()[0]->body().size(), 1);
+
+ ASSERT_EQ(e->else_statements()[1]->condition(), nullptr);
+ EXPECT_EQ(e->else_statements()[1]->body().size(), 0);
+}
+
+TEST_F(ParserImplTest, IfStmt_WithPremerge) {
+ ParserImpl p{R"(if (a == 4) {
+ a = b;
+ c = d;
+} else {
+ d = 2;
+} premerge {
+ a = 2;
+})"};
+ auto e = p.if_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsIf());
+ ASSERT_NE(e->condition(), nullptr);
+ ASSERT_TRUE(e->condition()->IsRelational());
+ EXPECT_EQ(e->body().size(), 2);
+
+ ASSERT_EQ(e->else_statements().size(), 1);
+ ASSERT_EQ(e->else_statements()[0]->condition(), nullptr);
+ EXPECT_EQ(e->else_statements()[0]->body().size(), 1);
+
+ ASSERT_EQ(e->premerge().size(), 1);
+}
+
+TEST_F(ParserImplTest, IfStmt_InvalidCondition) {
+ ParserImpl p{"if (a = 3) {}"};
+ auto e = p.if_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:7: expected )");
+}
+
+TEST_F(ParserImplTest, IfStmt_MissingCondition) {
+ ParserImpl p{"if {}"};
+ auto e = p.if_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:4: expected (");
+}
+
+TEST_F(ParserImplTest, IfStmt_InvalidBody) {
+ ParserImpl p{"if (a) { fn main() -> void {}}"};
+ auto e = p.if_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:10: missing }");
+}
+
+TEST_F(ParserImplTest, IfStmt_MissingBody) {
+ ParserImpl p{"if (a)"};
+ auto e = p.if_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:7: missing {");
+}
+
+TEST_F(ParserImplTest, IfStmt_InvalidElseif) {
+ ParserImpl p{"if (a) {} elseif (a) { fn main() -> a{}}"};
+ auto e = p.if_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:24: missing }");
+}
+
+TEST_F(ParserImplTest, IfStmt_InvalidElse) {
+ ParserImpl p{"if (a) {} else { fn main() -> a{}}"};
+ auto e = p.if_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:18: missing }");
+}
+
+TEST_F(ParserImplTest, IfStmt_InvalidPremerge) {
+ ParserImpl p{"if (a) {} else {} premerge { fn main() -> a{}}"};
+ auto e = p.if_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:30: missing }");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_import_decl_test.cc b/src/reader/wgsl/parser_impl_import_decl_test.cc
new file mode 100644
index 0000000..bfd6cc9
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_import_decl_test.cc
@@ -0,0 +1,95 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ImportDecl_Import) {
+ ParserImpl p{R"(import "GLSL.std.450" as glsl)"};
+
+ auto import = p.import_decl();
+ ASSERT_NE(import, nullptr);
+ ASSERT_FALSE(p.has_error()) << p.error();
+
+ EXPECT_EQ("GLSL.std.450", import->path());
+ EXPECT_EQ("glsl", import->name());
+ EXPECT_EQ(1, import->line());
+ EXPECT_EQ(1, import->column());
+}
+
+TEST_F(ParserImplTest, ImportDecl_Import_WithNamespace) {
+ ParserImpl p{R"(import "GLSL.std.450" as std::glsl)"};
+ auto import = p.import_decl();
+ ASSERT_NE(import, nullptr);
+ ASSERT_FALSE(p.has_error()) << p.error();
+ EXPECT_EQ("std::glsl", import->name());
+}
+
+TEST_F(ParserImplTest, ImportDecl_Invalid_MissingPath) {
+ ParserImpl p{R"(import as glsl)"};
+ auto import = p.import_decl();
+ ASSERT_EQ(import, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:8: missing path for import");
+}
+
+TEST_F(ParserImplTest, ImportDecl_Invalid_EmptyPath) {
+ ParserImpl p{R"(import "" as glsl)"};
+ auto import = p.import_decl();
+ ASSERT_EQ(import, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:8: import path must not be empty");
+}
+
+TEST_F(ParserImplTest, ImportDecl_Invalid_NameMissingTerminatingIdentifier) {
+ ParserImpl p{R"(import "GLSL.std.450" as glsl::)"};
+ auto import = p.import_decl();
+ ASSERT_EQ(import, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:32: invalid name for import");
+}
+
+TEST_F(ParserImplTest, ImportDecl_Invalid_NameInvalid) {
+ ParserImpl p{R"(import "GLSL.std.450" as 12glsl)"};
+ auto import = p.import_decl();
+ ASSERT_EQ(import, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:26: invalid name for import");
+}
+
+TEST_F(ParserImplTest, ImportDecl_Invalid_MissingName) {
+ ParserImpl p{R"(import "GLSL.std.450" as)"};
+ auto import = p.import_decl();
+ ASSERT_EQ(import, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:25: missing name for import");
+}
+
+TEST_F(ParserImplTest, ImportDecl_Invalid_MissingAs) {
+ ParserImpl p{R"(import "GLSL.std.450" glsl)"};
+ auto import = p.import_decl();
+ ASSERT_EQ(import, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:23: missing 'as' for import");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_inclusive_or_expression_test.cc b/src/reader/wgsl/parser_impl_inclusive_or_expression_test.cc
new file mode 100644
index 0000000..38223fb
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_inclusive_or_expression_test.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, InclusiveOrExpression_Parses) {
+ ParserImpl p{"a | true"};
+ auto e = p.inclusive_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kOr, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, InclusiveOrExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} | true"};
+ auto e = p.inclusive_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, InclusiveOrExpression_InvalidRHS) {
+ ParserImpl p{"true | if (a) {}"};
+ auto e = p.inclusive_or_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse right side of | expression");
+}
+
+TEST_F(ParserImplTest, InclusiveOrExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.inclusive_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_logical_and_expression_test.cc b/src/reader/wgsl/parser_impl_logical_and_expression_test.cc
new file mode 100644
index 0000000..c48cca5
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_logical_and_expression_test.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, LogicalAndExpression_Parses) {
+ ParserImpl p{"a && true"};
+ auto e = p.logical_and_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kLogicalAnd, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, LogicalAndExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} && true"};
+ auto e = p.logical_and_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, LogicalAndExpression_InvalidRHS) {
+ ParserImpl p{"true && if (a) {}"};
+ auto e = p.logical_and_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: unable to parse right side of && expression");
+}
+
+TEST_F(ParserImplTest, LogicalAndExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.logical_and_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_logical_or_expression_test.cc b/src/reader/wgsl/parser_impl_logical_or_expression_test.cc
new file mode 100644
index 0000000..9c90b66
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_logical_or_expression_test.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, LogicalOrExpression_Parses) {
+ ParserImpl p{"a || true"};
+ auto e = p.logical_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kLogicalOr, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, LogicalOrExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} || true"};
+ auto e = p.logical_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, LogicalOrExpression_InvalidRHS) {
+ ParserImpl p{"true || if (a) {}"};
+ auto e = p.logical_or_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: unable to parse right side of || expression");
+}
+
+TEST_F(ParserImplTest, LogicalOrExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.logical_or_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_loop_stmt_test.cc b/src/reader/wgsl/parser_impl_loop_stmt_test.cc
new file mode 100644
index 0000000..8896582
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_loop_stmt_test.cc
@@ -0,0 +1,102 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, LoopStmt_BodyNoContinuing) {
+ ParserImpl p{"loop { nop; }"};
+ auto e = p.loop_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_EQ(e->body().size(), 1);
+ EXPECT_TRUE(e->body()[0]->IsNop());
+
+ EXPECT_EQ(e->continuing().size(), 0);
+}
+
+TEST_F(ParserImplTest, LoopStmt_BodyWithContinuing) {
+ ParserImpl p{"loop { nop; continuing { kill; }}"};
+ auto e = p.loop_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_EQ(e->body().size(), 1);
+ EXPECT_TRUE(e->body()[0]->IsNop());
+
+ EXPECT_EQ(e->continuing().size(), 1);
+ EXPECT_TRUE(e->continuing()[0]->IsKill());
+}
+
+TEST_F(ParserImplTest, LoopStmt_NoBodyNoContinuing) {
+ ParserImpl p{"loop { }"};
+ auto e = p.loop_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_EQ(e->body().size(), 0);
+ ASSERT_EQ(e->continuing().size(), 0);
+}
+
+TEST_F(ParserImplTest, LoopStmt_NoBodyWithContinuing) {
+ ParserImpl p{"loop { continuing { kill; }}"};
+ auto e = p.loop_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_EQ(e->body().size(), 0);
+ ASSERT_EQ(e->continuing().size(), 1);
+ EXPECT_TRUE(e->continuing()[0]->IsKill());
+}
+
+TEST_F(ParserImplTest, LoopStmt_MissingBracketLeft) {
+ ParserImpl p{"loop kill; }"};
+ auto e = p.loop_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing { for loop");
+}
+
+TEST_F(ParserImplTest, LoopStmt_MissingBracketRight) {
+ ParserImpl p{"loop { kill; "};
+ auto e = p.loop_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: missing } for loop");
+}
+
+TEST_F(ParserImplTest, LoopStmt_InvalidStatements) {
+ ParserImpl p{"loop { kill }"};
+ auto e = p.loop_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: missing ;");
+}
+
+TEST_F(ParserImplTest, LoopStmt_InvalidContinuing) {
+ ParserImpl p{"loop { continuing { kill }}"};
+ auto e = p.loop_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:26: missing ;");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_multiplicative_expression_test.cc b/src/reader/wgsl/parser_impl_multiplicative_expression_test.cc
new file mode 100644
index 0000000..bf32a1d
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_multiplicative_expression_test.cc
@@ -0,0 +1,119 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Multiply) {
+ ParserImpl p{"a * true"};
+ auto e = p.multiplicative_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kMultiply, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Divide) {
+ ParserImpl p{"a / true"};
+ auto e = p.multiplicative_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kDivide, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, MultiplicativeExpression_Parses_Modulo) {
+ ParserImpl p{"a % true"};
+ auto e = p.multiplicative_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kModulo, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, MultiplicativeExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} * true"};
+ auto e = p.multiplicative_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, MultiplicativeExpression_InvalidRHS) {
+ ParserImpl p{"true * if (a) {}"};
+ auto e = p.multiplicative_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse right side of * expression");
+}
+
+TEST_F(ParserImplTest, MultiplicativeExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.multiplicative_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_param_list_test.cc b/src/reader/wgsl/parser_impl_param_list_test.cc
new file mode 100644
index 0000000..05f4acd
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_param_list_test.cc
@@ -0,0 +1,85 @@
+// Copyright 2020 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 <memory>
+
+#include "gtest/gtest.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/vector_type.h"
+#include "src/ast/variable.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ParamList_Single) {
+ auto tm = TypeManager::Instance();
+ auto i32 = tm->Get(std::make_unique<ast::type::I32Type>());
+
+ ParserImpl p{"a : i32"};
+ auto e = p.param_list();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ EXPECT_EQ(e.size(), 1);
+
+ EXPECT_EQ(e[0]->name(), "a");
+ EXPECT_EQ(e[0]->type(), i32);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, ParamList_Multiple) {
+ auto tm = TypeManager::Instance();
+ auto i32 = tm->Get(std::make_unique<ast::type::I32Type>());
+ auto f32 = tm->Get(std::make_unique<ast::type::F32Type>());
+ auto vec2 = tm->Get(std::make_unique<ast::type::VectorType>(f32, 2));
+
+ ParserImpl p{"a : i32, b: f32, c: vec2<f32>"};
+ auto e = p.param_list();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ EXPECT_EQ(e.size(), 3);
+
+ EXPECT_EQ(e[0]->name(), "a");
+ EXPECT_EQ(e[0]->type(), i32);
+
+ EXPECT_EQ(e[1]->name(), "b");
+ EXPECT_EQ(e[1]->type(), f32);
+
+ EXPECT_EQ(e[2]->name(), "c");
+ EXPECT_EQ(e[2]->type(), vec2);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, ParamList_Empty) {
+ ParserImpl p{""};
+ auto e = p.param_list();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ EXPECT_EQ(e.size(), 0);
+}
+
+TEST_F(ParserImplTest, ParamList_HangingComma) {
+ ParserImpl p{"a : i32,"};
+ auto e = p.param_list();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:8: found , but no variable declaration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc b/src/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
new file mode 100644
index 0000000..42cae73
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ParenRhsStmt) {
+ ParserImpl p{"(a + b)"};
+ auto e = p.paren_rhs_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsRelational());
+}
+
+TEST_F(ParserImplTest, ParenRhsStmt_MissingLeftParen) {
+ ParserImpl p{"true)"};
+ auto e = p.paren_rhs_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:1: expected (");
+}
+
+TEST_F(ParserImplTest, ParenRhsStmt_MissingRightParen) {
+ ParserImpl p{"(true"};
+ auto e = p.paren_rhs_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: expected )");
+}
+
+TEST_F(ParserImplTest, ParenRhsStmt_InvalidExpression) {
+ ParserImpl p{"(if (a() {})"};
+ auto e = p.paren_rhs_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:2: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, ParenRhsStmt_MissingExpression) {
+ ParserImpl p{"()"};
+ auto e = p.paren_rhs_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:2: unable to parse expression");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_pipeline_stage_test.cc b/src/reader/wgsl/parser_impl_pipeline_stage_test.cc
new file mode 100644
index 0000000..fc4983c
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_pipeline_stage_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/pipeline_stage.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+struct PipelineStageData {
+ const char* input;
+ ast::PipelineStage result;
+};
+inline std::ostream& operator<<(std::ostream& out, PipelineStageData data) {
+ out << std::string(data.input);
+ return out;
+}
+using PipelineStageTest = testing::TestWithParam<PipelineStageData>;
+TEST_P(PipelineStageTest, Parses) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+
+ auto stage = p.pipeline_stage();
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(stage, params.result);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsEof());
+}
+INSTANTIATE_TEST_SUITE_P(
+ ParserImplTest,
+ PipelineStageTest,
+ testing::Values(
+ PipelineStageData{"vertex", ast::PipelineStage::kVertex},
+ PipelineStageData{"fragment", ast::PipelineStage::kFragment},
+ PipelineStageData{"compute", ast::PipelineStage::kCompute}));
+
+TEST_F(ParserImplTest, PipelineStage_NoMatch) {
+ ParserImpl p{"not-a-stage"};
+ auto stage = p.pipeline_stage();
+ ASSERT_EQ(stage, ast::PipelineStage::kNone);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "not");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_postfix_expression_test.cc b/src/reader/wgsl/parser_impl_postfix_expression_test.cc
new file mode 100644
index 0000000..01782ee
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_postfix_expression_test.cc
@@ -0,0 +1,200 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/call_expression.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/member_accessor_expression.h"
+#include "src/ast/unary_derivative_expression.h"
+#include "src/ast/unary_method_expression.h"
+#include "src/ast/unary_op_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, PostfixExpression_Array_ConstantIndex) {
+ ParserImpl p{"a[1]"};
+ auto e = p.postfix_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsArrayAccessor());
+ auto ary = e->AsArrayAccessor();
+
+ ASSERT_TRUE(ary->array()->IsIdentifier());
+ auto ident = ary->array()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(ary->idx_expr()->IsInitializer());
+ ASSERT_TRUE(ary->idx_expr()->AsInitializer()->IsConstInitializer());
+ auto c = ary->idx_expr()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(c->literal()->IsInt());
+ EXPECT_EQ(c->literal()->AsInt()->value(), 1);
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Array_ExpressionIndex) {
+ ParserImpl p{"a[1 + b / 4]"};
+ auto e = p.postfix_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsArrayAccessor());
+ auto ary = e->AsArrayAccessor();
+
+ ASSERT_TRUE(ary->array()->IsIdentifier());
+ auto ident = ary->array()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(ary->idx_expr()->IsRelational());
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Array_MissingIndex) {
+ ParserImpl p{"a[]"};
+ auto e = p.postfix_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:3: unable to parse expression inside []");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Array_MissingRightBrace) {
+ ParserImpl p{"a[1"};
+ auto e = p.postfix_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:4: missing ] for array accessor");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Array_InvalidIndex) {
+ ParserImpl p{"a[if(a() {})]"};
+ auto e = p.postfix_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:3: unable to parse expression inside []");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Call_Empty) {
+ ParserImpl p{"a()"};
+ auto e = p.postfix_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsCall());
+ auto c = e->AsCall();
+
+ ASSERT_TRUE(c->func()->IsIdentifier());
+ auto func = c->func()->AsIdentifier();
+ ASSERT_EQ(func->name().size(), 1);
+ EXPECT_EQ(func->name()[0], "a");
+
+ EXPECT_EQ(c->params().size(), 0);
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Call_WithArgs) {
+ ParserImpl p{"std::test(1, b, 2 + 3 / b)"};
+ auto e = p.postfix_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsCall());
+ auto c = e->AsCall();
+
+ ASSERT_TRUE(c->func()->IsIdentifier());
+ auto func = c->func()->AsIdentifier();
+ ASSERT_EQ(func->name().size(), 2);
+ EXPECT_EQ(func->name()[0], "std");
+ EXPECT_EQ(func->name()[1], "test");
+
+ EXPECT_EQ(c->params().size(), 3);
+ EXPECT_TRUE(c->params()[0]->IsInitializer());
+ EXPECT_TRUE(c->params()[1]->IsIdentifier());
+ EXPECT_TRUE(c->params()[2]->IsRelational());
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Call_InvalidArg) {
+ ParserImpl p{"a(if(a) {})"};
+ auto e = p.postfix_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:3: unable to parse argument expression");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Call_HangingComma) {
+ ParserImpl p{"a(b, )"};
+ auto e = p.postfix_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: unable to parse argument expression after comma");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_Call_MissingRightParen) {
+ ParserImpl p{"a("};
+ auto e = p.postfix_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:3: missing ) for call expression");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_MemberAccessor) {
+ ParserImpl p{"a.b"};
+ auto e = p.postfix_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsMemberAccessor());
+
+ auto m = e->AsMemberAccessor();
+ ASSERT_TRUE(m->structure()->IsIdentifier());
+ ASSERT_EQ(m->structure()->AsIdentifier()->name().size(), 1);
+ EXPECT_EQ(m->structure()->AsIdentifier()->name()[0], "a");
+
+ ASSERT_TRUE(m->member()->IsIdentifier());
+ ASSERT_EQ(m->member()->AsIdentifier()->name().size(), 1);
+ EXPECT_EQ(m->member()->AsIdentifier()->name()[0], "b");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_MemberAccesssor_InvalidIdent) {
+ ParserImpl p{"a.if"};
+ auto e = p.postfix_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:3: missing identifier for member accessor");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_MemberAccessor_MissingIdent) {
+ ParserImpl p{"a."};
+ auto e = p.postfix_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:3: missing identifier for member accessor");
+}
+
+TEST_F(ParserImplTest, PostfixExpression_NonMatch_returnLHS) {
+ ParserImpl p{"a b"};
+ auto e = p.postfix_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_premerge_stmt_test.cc b/src/reader/wgsl/parser_impl_premerge_stmt_test.cc
new file mode 100644
index 0000000..55c3744
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_premerge_stmt_test.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, PremergeStmt) {
+ ParserImpl p{"premerge { nop; }"};
+ auto e = p.premerge_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 1);
+ ASSERT_TRUE(e[0]->IsNop());
+}
+
+TEST_F(ParserImplTest, PremergeStmt_InvalidBody) {
+ ParserImpl p{"premerge { nop }"};
+ auto e = p.premerge_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e.size(), 0);
+ EXPECT_EQ(p.error(), "1:16: missing ;");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_primary_expression_test.cc b/src/reader/wgsl/parser_impl_primary_expression_test.cc
new file mode 100644
index 0000000..f9d676a
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_primary_expression_test.cc
@@ -0,0 +1,335 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/as_expression.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/cast_expression.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type_initializer_expression.h"
+#include "src/ast/unary_derivative_expression.h"
+#include "src/ast/unary_method_expression.h"
+#include "src/ast/unary_op_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, PrimaryExpression_Ident) {
+ ParserImpl p{"a"};
+ auto e = p.primary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+ auto ident = e->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Ident_WithNamespace) {
+ ParserImpl p{"a::b::c::d"};
+ auto e = p.primary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+ auto ident = e->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 4);
+ EXPECT_EQ(ident->name()[0], "a");
+ EXPECT_EQ(ident->name()[1], "b");
+ EXPECT_EQ(ident->name()[2], "c");
+ EXPECT_EQ(ident->name()[3], "d");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Ident_MissingIdent) {
+ ParserImpl p{"a::"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:4: identifier expected");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_TypeDecl) {
+ ParserImpl p{"vec4<i32>(1, 2, 3, 4))"};
+ auto e = p.primary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsInitializer());
+ ASSERT_TRUE(e->AsInitializer()->IsTypeInitializer());
+ auto ty = e->AsInitializer()->AsTypeInitializer();
+
+ ASSERT_EQ(ty->values().size(), 4);
+ const auto& val = ty->values();
+ ASSERT_TRUE(val[0]->IsInitializer());
+ ASSERT_TRUE(val[0]->AsInitializer()->IsConstInitializer());
+ auto ident = val[0]->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(ident->literal()->IsInt());
+ EXPECT_EQ(ident->literal()->AsInt()->value(), 1);
+
+ ASSERT_TRUE(val[1]->IsInitializer());
+ ASSERT_TRUE(val[1]->AsInitializer()->IsConstInitializer());
+ ident = val[1]->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(ident->literal()->IsInt());
+ EXPECT_EQ(ident->literal()->AsInt()->value(), 2);
+
+ ASSERT_TRUE(val[2]->IsInitializer());
+ ASSERT_TRUE(val[2]->AsInitializer()->IsConstInitializer());
+ ident = val[2]->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(ident->literal()->IsInt());
+ EXPECT_EQ(ident->literal()->AsInt()->value(), 3);
+
+ ASSERT_TRUE(val[3]->IsInitializer());
+ ASSERT_TRUE(val[3]->AsInitializer()->IsConstInitializer());
+ ident = val[3]->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(ident->literal()->IsInt());
+ EXPECT_EQ(ident->literal()->AsInt()->value(), 4);
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_InvalidTypeDecl) {
+ ParserImpl p{"vec4<if>(2., 3., 4., 5.)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: unable to determine subtype for vector");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_MissingLeftParen) {
+ ParserImpl p{"vec4<f32> 2., 3., 4., 5.)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing ( for type initializer");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_MissingRightParen) {
+ ParserImpl p{"vec4<f32>(2., 3., 4., 5."};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:25: missing ) for type initializer");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_TypeDecl_InvalidValue) {
+ ParserImpl p{"i32(if(a) {})"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: unable to parse argument expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_ConstLiteral_True) {
+ ParserImpl p{"true"};
+ auto e = p.primary_expression();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsInitializer());
+ ASSERT_TRUE(e->AsInitializer()->IsConstInitializer());
+ auto init = e->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ EXPECT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_ParenExpr) {
+ ParserImpl p{"(a == b)"};
+ auto e = p.primary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsRelational());
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_ParenExpr_MissingRightParen) {
+ ParserImpl p{"(a == b"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: expected )");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_ParenExpr_MissingExpr) {
+ ParserImpl p{"()"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:2: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_ParenExpr_InvalidExpr) {
+ ParserImpl p{"(if (a) {})"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:2: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Cast) {
+ auto tm = TypeManager::Instance();
+ auto f32_type = tm->Get(std::make_unique<ast::type::F32Type>());
+
+ ParserImpl p{"cast<f32>(1)"};
+ auto e = p.primary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsCast());
+
+ auto c = e->AsCast();
+ ASSERT_EQ(c->type(), f32_type);
+
+ ASSERT_TRUE(c->expr()->IsInitializer());
+ ASSERT_TRUE(c->expr()->AsInitializer()->IsConstInitializer());
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingGreaterThan) {
+ ParserImpl p{"cast<f32(1)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: missing > for cast expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingType) {
+ ParserImpl p{"cast<>(1)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing type for cast expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Cast_InvalidType) {
+ ParserImpl p{"cast<invalid>(1)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: unknown type alias 'invalid'");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingLeftParen) {
+ ParserImpl p{"cast<f32>1)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:10: expected (");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingRightParen) {
+ ParserImpl p{"cast<f32>(1"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: expected )");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Cast_MissingExpression) {
+ ParserImpl p{"cast<f32>()"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_Cast_InvalidExpression) {
+ ParserImpl p{"cast<f32>(if (a) {})"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_As) {
+ auto tm = TypeManager::Instance();
+ auto f32_type = tm->Get(std::make_unique<ast::type::F32Type>());
+
+ ParserImpl p{"as<f32>(1)"};
+ auto e = p.primary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsAs());
+
+ auto c = e->AsAs();
+ ASSERT_EQ(c->type(), f32_type);
+
+ ASSERT_TRUE(c->expr()->IsInitializer());
+ ASSERT_TRUE(c->expr()->AsInitializer()->IsConstInitializer());
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_As_MissingGreaterThan) {
+ ParserImpl p{"as<f32(1)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:7: missing > for as expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_As_MissingType) {
+ ParserImpl p{"as<>(1)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:4: missing type for as expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_As_InvalidType) {
+ ParserImpl p{"as<invalid>(1)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:4: unknown type alias 'invalid'");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_As_MissingLeftParen) {
+ ParserImpl p{"as<f32>1)"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: expected (");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_As_MissingRightParen) {
+ ParserImpl p{"as<f32>(1"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:10: expected )");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_As_MissingExpression) {
+ ParserImpl p{"as<f32>()"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, PrimaryExpression_As_InvalidExpression) {
+ ParserImpl p{"as<f32>(if (a) {})"};
+ auto e = p.primary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: unable to parse expression");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_regardless_stmt_test.cc b/src/reader/wgsl/parser_impl_regardless_stmt_test.cc
new file mode 100644
index 0000000..ea2f05b
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_regardless_stmt_test.cc
@@ -0,0 +1,62 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, RegardlessStmt) {
+ ParserImpl p{"regardless (a) { kill; }"};
+ auto e = p.regardless_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsRegardless());
+ ASSERT_NE(e->condition(), nullptr);
+ EXPECT_TRUE(e->condition()->IsIdentifier());
+ ASSERT_EQ(e->body().size(), 1);
+ EXPECT_TRUE(e->body()[0]->IsKill());
+}
+
+TEST_F(ParserImplTest, RegardlessStmt_InvalidCondition) {
+ ParserImpl p{"regardless(if(a){}) {}"};
+ auto e = p.regardless_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, RegardlessStmt_EmptyCondition) {
+ ParserImpl p{"regardless() {}"};
+ auto e = p.regardless_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, RegardlessStmt_InvalidBody) {
+ ParserImpl p{"regardless(a + 2 - 5 == true) { kill }"};
+ auto e = p.regardless_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:38: missing ;");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_relational_expression_test.cc b/src/reader/wgsl/parser_impl_relational_expression_test.cc
new file mode 100644
index 0000000..8f236b0
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_relational_expression_test.cc
@@ -0,0 +1,141 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, RelationalExpression_Parses_LessThan) {
+ ParserImpl p{"a < true"};
+ auto e = p.relational_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kLessThan, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, RelationalExpression_Parses_GreaterThan) {
+ ParserImpl p{"a > true"};
+ auto e = p.relational_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kGreaterThan, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, RelationalExpression_Parses_LessThanEqual) {
+ ParserImpl p{"a <= true"};
+ auto e = p.relational_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kLessThanEqual, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, RelationalExpression_Parses_GreaterThanEqual) {
+ ParserImpl p{"a >= true"};
+ auto e = p.relational_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kGreaterThanEqual, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, RelationalExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} < true"};
+ auto e = p.relational_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, RelationalExpression_InvalidRHS) {
+ ParserImpl p{"true < if (a) {}"};
+ auto e = p.relational_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse right side of < expression");
+}
+
+TEST_F(ParserImplTest, RelationalExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.relational_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_shift_expression_test.cc b/src/reader/wgsl/parser_impl_shift_expression_test.cc
new file mode 100644
index 0000000..21fe620
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_shift_expression_test.cc
@@ -0,0 +1,119 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/relational_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, ShiftExpression_Parses_ShiftLeft) {
+ ParserImpl p{"a << true"};
+ auto e = p.shift_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kShiftLeft, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, ShiftExpression_Parses_ShiftRight) {
+ ParserImpl p{"a >> true"};
+ auto e = p.shift_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kShiftRight, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, ShiftExpression_Parses_ShiftRightArith) {
+ ParserImpl p{"a >>> true"};
+ auto e = p.shift_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsRelational());
+ auto rel = e->AsRelational();
+ EXPECT_EQ(ast::Relation::kShiftRightArith, rel->relation());
+
+ ASSERT_TRUE(rel->lhs()->IsIdentifier());
+ auto ident = rel->lhs()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(rel->rhs()->IsInitializer());
+ ASSERT_TRUE(rel->rhs()->AsInitializer()->IsConstInitializer());
+ auto init = rel->rhs()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsBool());
+ ASSERT_TRUE(init->literal()->AsBool()->IsTrue());
+}
+
+TEST_F(ParserImplTest, ShiftExpression_InvalidLHS) {
+ ParserImpl p{"if (a) {} << true"};
+ auto e = p.shift_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, ShiftExpression_InvalidRHS) {
+ ParserImpl p{"true << if (a) {}"};
+ auto e = p.shift_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: unable to parse right side of << expression");
+}
+
+TEST_F(ParserImplTest, ShiftExpression_NoOr_ReturnsLHS) {
+ ParserImpl p{"a true"};
+ auto e = p.shift_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIdentifier());
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_statement_test.cc b/src/reader/wgsl/parser_impl_statement_test.cc
new file mode 100644
index 0000000..005eb82
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_statement_test.cc
@@ -0,0 +1,290 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/return_statement.h"
+#include "src/ast/statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, Statement) {
+ ParserImpl p{"return;"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ EXPECT_TRUE(e->IsReturn());
+}
+
+TEST_F(ParserImplTest, Statement_Semicolon) {
+ ParserImpl p{";"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e, nullptr);
+}
+
+TEST_F(ParserImplTest, Statement_Return_NoValue) {
+ ParserImpl p{"return;"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsReturn());
+ auto ret = e->AsReturn();
+ ASSERT_EQ(ret->value(), nullptr);
+}
+
+TEST_F(ParserImplTest, Statement_Return_Value) {
+ ParserImpl p{"return a + b * (.1 - .2);"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsReturn());
+ auto ret = e->AsReturn();
+ ASSERT_NE(ret->value(), nullptr);
+ EXPECT_TRUE(ret->value()->IsRelational());
+}
+
+TEST_F(ParserImplTest, Statement_Return_MissingSemi) {
+ ParserImpl p{"return"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:7: missing ;");
+}
+
+TEST_F(ParserImplTest, Statement_Return_Invalid) {
+ ParserImpl p{"return if(a) {};"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing ;");
+}
+
+TEST_F(ParserImplTest, Statement_If) {
+ ParserImpl p{"if (a) {}"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsIf());
+}
+
+TEST_F(ParserImplTest, Statement_If_Invalid) {
+ ParserImpl p{"if (a) { fn main() -> {}}"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:10: missing }");
+}
+
+TEST_F(ParserImplTest, Statement_Unless) {
+ ParserImpl p{"unless (a) {}"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnless());
+}
+
+TEST_F(ParserImplTest, Statement_Unless_Invalid) {
+ ParserImpl p{"unless () {}"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, Statement_Regardless) {
+ ParserImpl p{"regardless (a) {}"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsRegardless());
+}
+
+TEST_F(ParserImplTest, Statement_Regardless_Invalid) {
+ ParserImpl p{"regardless () {}"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, Statement_Variable) {
+ ParserImpl p{"var a : i32 = 1;"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsVariable());
+}
+
+TEST_F(ParserImplTest, Statement_Variable_Invalid) {
+ ParserImpl p{"var a : i32 =;"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: missing initializer for variable declaration");
+}
+
+TEST_F(ParserImplTest, Statement_Variable_MissingSemicolon) {
+ ParserImpl p{"var a : i32"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: missing ;");
+}
+
+TEST_F(ParserImplTest, Statement_Switch) {
+ ParserImpl p{"switch (a) {}"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsSwitch());
+}
+
+TEST_F(ParserImplTest, Statement_Switch_Invalid) {
+ ParserImpl p{"switch (a) { case: {}}"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:18: unable to parse case conditional");
+}
+
+TEST_F(ParserImplTest, Statement_Loop) {
+ ParserImpl p{"loop {}"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsLoop());
+}
+
+TEST_F(ParserImplTest, Statement_Loop_Invalid) {
+ ParserImpl p{"loop kill; }"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing { for loop");
+}
+
+TEST_F(ParserImplTest, Statement_Assignment) {
+ ParserImpl p{"a = b;"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ EXPECT_TRUE(e->IsAssign());
+}
+
+TEST_F(ParserImplTest, Statement_Assignment_Invalid) {
+ ParserImpl p{"a = if(b) {};"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: unable to parse right side of assignment");
+}
+
+TEST_F(ParserImplTest, Statement_Assignment_MissingSemicolon) {
+ ParserImpl p{"a = b"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing ;");
+}
+
+TEST_F(ParserImplTest, Statement_Break) {
+ ParserImpl p{"break;"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ EXPECT_TRUE(e->IsBreak());
+}
+
+TEST_F(ParserImplTest, Statement_Break_Invalid) {
+ ParserImpl p{"break if (a = b);"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: expected )");
+}
+
+TEST_F(ParserImplTest, Statement_Break_MissingSemicolon) {
+ ParserImpl p{"break if (a == b)"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:18: missing ;");
+}
+
+TEST_F(ParserImplTest, Statement_Continue) {
+ ParserImpl p{"continue;"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ EXPECT_TRUE(e->IsContinue());
+}
+
+TEST_F(ParserImplTest, Statement_Continue_Invalid) {
+ ParserImpl p{"continue if (a = b);"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:16: expected )");
+}
+
+TEST_F(ParserImplTest, Statement_Continue_MissingSemicolon) {
+ ParserImpl p{"continue if (a == b)"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:21: missing ;");
+}
+
+TEST_F(ParserImplTest, Statement_Kill) {
+ ParserImpl p{"kill;"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ EXPECT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsKill());
+}
+
+TEST_F(ParserImplTest, Statement_Kill_MissingSemicolon) {
+ ParserImpl p{"kill"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing ;");
+}
+
+TEST_F(ParserImplTest, Statement_Nop) {
+ ParserImpl p{"nop;"};
+ auto e = p.statement();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ EXPECT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsNop());
+}
+
+TEST_F(ParserImplTest, Statement_Nop_MissingSemicolon) {
+ ParserImpl p{"nop"};
+ auto e = p.statement();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:4: missing ;");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_statements_test.cc b/src/reader/wgsl/parser_impl_statements_test.cc
new file mode 100644
index 0000000..f993794
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_statements_test.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, Statements) {
+ ParserImpl p{"nop; kill; return;"};
+ auto e = p.statements();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 3);
+ EXPECT_TRUE(e[0]->IsNop());
+ EXPECT_TRUE(e[1]->IsKill());
+ EXPECT_TRUE(e[2]->IsReturn());
+}
+
+TEST_F(ParserImplTest, Statements_Empty) {
+ ParserImpl p{""};
+ auto e = p.statements();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_EQ(e.size(), 0);
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_storage_class_test.cc b/src/reader/wgsl/parser_impl_storage_class_test.cc
new file mode 100644
index 0000000..f479d00
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_storage_class_test.cc
@@ -0,0 +1,73 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/storage_class.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+struct StorageClassData {
+ const char* input;
+ ast::StorageClass result;
+};
+inline std::ostream& operator<<(std::ostream& out, StorageClassData data) {
+ out << std::string(data.input);
+ return out;
+}
+using StorageClassTest = testing::TestWithParam<StorageClassData>;
+TEST_P(StorageClassTest, Parses) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+
+ auto sc = p.storage_class();
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(sc, params.result);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsEof());
+}
+INSTANTIATE_TEST_SUITE_P(
+ ParserImplTest,
+ StorageClassTest,
+ testing::Values(
+ StorageClassData{"in", ast::StorageClass::kInput},
+ StorageClassData{"out", ast::StorageClass::kOutput},
+ StorageClassData{"uniform", ast::StorageClass::kUniform},
+ StorageClassData{"workgroup", ast::StorageClass::kWorkgroup},
+ StorageClassData{"uniform_constant",
+ ast::StorageClass::kUniformConstant},
+ StorageClassData{"storage_buffer", ast::StorageClass::kStorageBuffer},
+ StorageClassData{"image", ast::StorageClass::kImage},
+ StorageClassData{"push_constant", ast::StorageClass::kPushConstant},
+ StorageClassData{"private", ast::StorageClass::kPrivate},
+ StorageClassData{"function", ast::StorageClass::kFunction}));
+
+TEST_F(ParserImplTest, StorageClass_NoMatch) {
+ ParserImpl p{"not-a-storage-class"};
+ auto sc = p.storage_class();
+ ASSERT_EQ(sc, ast::StorageClass::kNone);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "not");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_struct_body_decl_test.cc b/src/reader/wgsl/parser_impl_struct_body_decl_test.cc
new file mode 100644
index 0000000..62c4f57
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_struct_body_decl_test.cc
@@ -0,0 +1,80 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, StructBodyDecl_Parses) {
+ auto i32 =
+ TypeManager::Instance()->Get(std::make_unique<ast::type::I32Type>());
+
+ ParserImpl p{"{a : i32;}"};
+ auto m = p.struct_body_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_EQ(m.size(), 1);
+
+ const auto& mem = m[0];
+ EXPECT_EQ(mem->name(), "a");
+ EXPECT_EQ(mem->type(), i32);
+ EXPECT_EQ(mem->decorations().size(), 0);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, StructBodyDecl_ParsesEmpty) {
+ ParserImpl p{"{}"};
+ auto m = p.struct_body_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_EQ(m.size(), 0);
+}
+
+TEST_F(ParserImplTest, StructBodyDecl_InvalidMember) {
+ ParserImpl p{R"(
+{
+ [[offset nan]] a : i32;
+})"};
+ auto m = p.struct_body_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "3:12: invalid value for offset decoration");
+}
+
+TEST_F(ParserImplTest, StructBodyDecl_MissingClosingBracket) {
+ ParserImpl p{"{a : i32;"};
+ auto m = p.struct_body_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:10: missing } for struct declaration");
+}
+
+TEST_F(ParserImplTest, StructBodyDecl_InvalidToken) {
+ ParserImpl p{R"(
+{
+ a : i32;
+ 1.23
+} )"};
+ auto m = p.struct_body_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "4:3: invalid identifier declaration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_struct_decl_test.cc b/src/reader/wgsl/parser_impl_struct_decl_test.cc
new file mode 100644
index 0000000..87e98cb
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_struct_decl_test.cc
@@ -0,0 +1,95 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/type/struct_type.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, StructDecl_Parses) {
+ ParserImpl p{R"(
+struct {
+ a : i32;
+ [[offset 4 ]] b : f32;
+})"};
+ auto s = p.struct_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(s, nullptr);
+ ASSERT_EQ(s->impl()->members().size(), 2);
+ EXPECT_EQ(s->impl()->members()[0]->name(), "a");
+ EXPECT_EQ(s->impl()->members()[1]->name(), "b");
+}
+
+TEST_F(ParserImplTest, StructDecl_ParsesWithDecoration) {
+ ParserImpl p{R"(
+[[block]] struct {
+ a : f32;
+ b : f32;
+})"};
+ auto s = p.struct_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(s, nullptr);
+ ASSERT_EQ(s->impl()->members().size(), 2);
+ EXPECT_EQ(s->impl()->members()[0]->name(), "a");
+ EXPECT_EQ(s->impl()->members()[1]->name(), "b");
+}
+
+TEST_F(ParserImplTest, StructDecl_EmptyMembers) {
+ ParserImpl p{"struct {}"};
+ auto s = p.struct_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(s, nullptr);
+ ASSERT_EQ(s->impl()->members().size(), 0);
+}
+
+TEST_F(ParserImplTest, StructDecl_MissingBracketLeft) {
+ ParserImpl p{"struct }"};
+ auto s = p.struct_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(s, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing { for struct declaration");
+}
+
+TEST_F(ParserImplTest, StructDecl_InvalidStructBody) {
+ ParserImpl p{"struct { a : B; }"};
+ auto s = p.struct_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(s, nullptr);
+ EXPECT_EQ(p.error(), "1:14: unknown type alias 'B'");
+}
+
+TEST_F(ParserImplTest, StructDecl_InvalidStructDecorationDecl) {
+ ParserImpl p{"[[block struct { a : i32; }"};
+ auto s = p.struct_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(s, nullptr);
+ EXPECT_EQ(p.error(), "1:9: missing ]] for struct decoration");
+}
+
+TEST_F(ParserImplTest, StructDecl_MissingStruct) {
+ ParserImpl p{"[[block]] {}"};
+ auto s = p.struct_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(s, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing struct declaration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_struct_decoration_decl_test.cc b/src/reader/wgsl/parser_impl_struct_decoration_decl_test.cc
new file mode 100644
index 0000000..d6e1cc4
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_struct_decoration_decl_test.cc
@@ -0,0 +1,47 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, StructDecorationDecl_Parses) {
+ ParserImpl p{"[[block]]"};
+ auto d = p.struct_decoration_decl();
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(d, ast::StructDecoration::kBlock);
+}
+
+TEST_F(ParserImplTest, StructDecorationDecl_MissingAttrRight) {
+ ParserImpl p{"[[block"};
+ p.struct_decoration_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:8: missing ]] for struct decoration");
+}
+
+TEST_F(ParserImplTest, StructDecorationDecl_InvalidDecoration) {
+ ParserImpl p{"[[invalid]]"};
+ p.struct_decoration_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:3: unknown struct decoration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_struct_decoration_test.cc b/src/reader/wgsl/parser_impl_struct_decoration_test.cc
new file mode 100644
index 0000000..3f5aec3
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_struct_decoration_test.cc
@@ -0,0 +1,62 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/struct_decoration.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+struct StructDecorationData {
+ const char* input;
+ ast::StructDecoration result;
+};
+inline std::ostream& operator<<(std::ostream& out, StructDecorationData data) {
+ out << std::string(data.input);
+ return out;
+}
+using StructDecorationTest = testing::TestWithParam<StructDecorationData>;
+TEST_P(StructDecorationTest, Parses) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+
+ auto deco = p.struct_decoration();
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(deco, params.result);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsEof());
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ StructDecorationTest,
+ testing::Values(StructDecorationData{
+ "block", ast::StructDecoration::kBlock}));
+
+TEST_F(ParserImplTest, StructDecoration_NoMatch) {
+ ParserImpl p{"not-a-stage"};
+ auto deco = p.struct_decoration();
+ ASSERT_EQ(deco, ast::StructDecoration::kNone);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsIdentifier());
+ EXPECT_EQ(t.to_str(), "not");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc b/src/reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc
new file mode 100644
index 0000000..3e8069d
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_struct_member_decoration_decl_test.cc
@@ -0,0 +1,70 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/struct_member_offset_decoration.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, StructMemberDecorationDecl_EmptyStr) {
+ ParserImpl p{""};
+ auto deco = p.struct_member_decoration_decl();
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(deco.size(), 0);
+}
+
+TEST_F(ParserImplTest, StructMemberDecorationDecl_EmptyBlock) {
+ ParserImpl p{"[[]]"};
+ auto deco = p.struct_member_decoration_decl();
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:3: empty struct member decoration found");
+}
+
+TEST_F(ParserImplTest, StructMemberDecorationDecl_Single) {
+ ParserImpl p{"[[offset 4]]"};
+ auto deco = p.struct_member_decoration_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_EQ(deco.size(), 1);
+ EXPECT_TRUE(deco[0]->IsOffset());
+}
+
+TEST_F(ParserImplTest, StructMemberDecorationDecl_HandlesDuplicate) {
+ ParserImpl p{"[[offset 2, offset 4]]"};
+ auto deco = p.struct_member_decoration_decl();
+ ASSERT_TRUE(p.has_error()) << p.error();
+ EXPECT_EQ(p.error(), "1:21: duplicate offset decoration found");
+}
+
+TEST_F(ParserImplTest, StructMemberDecorationDecl_InvalidDecoration) {
+ ParserImpl p{"[[offset nan]]"};
+ auto deco = p.struct_member_decoration_decl();
+ ASSERT_TRUE(p.has_error()) << p.error();
+ EXPECT_EQ(p.error(), "1:10: invalid value for offset decoration");
+}
+
+TEST_F(ParserImplTest, StructMemberDecorationDecl_MissingClose) {
+ ParserImpl p{"[[offset 4"};
+ auto deco = p.struct_member_decoration_decl();
+ ASSERT_TRUE(p.has_error()) << p.error();
+ EXPECT_EQ(p.error(), "1:11: missing ]] for struct member decoration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_struct_member_decoration_test.cc b/src/reader/wgsl/parser_impl_struct_member_decoration_test.cc
new file mode 100644
index 0000000..14a760c
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_struct_member_decoration_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/struct_member_offset_decoration.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, StructMemberDecoration_Offset) {
+ ParserImpl p{"offset 4"};
+ auto deco = p.struct_member_decoration();
+ ASSERT_NE(deco, nullptr);
+ ASSERT_FALSE(p.has_error());
+ ASSERT_TRUE(deco->IsOffset());
+
+ auto o = deco->AsOffset();
+ EXPECT_EQ(o->offset(), 4);
+}
+
+TEST_F(ParserImplTest, StructMemberDecoration_Offset_MissingValue) {
+ ParserImpl p{"offset"};
+ auto deco = p.struct_member_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:7: invalid value for offset decoration");
+}
+
+TEST_F(ParserImplTest, StructMemberDecoration_Offset_MissingInvalid) {
+ ParserImpl p{"offset nan"};
+ auto deco = p.struct_member_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:8: invalid value for offset decoration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_struct_member_test.cc b/src/reader/wgsl/parser_impl_struct_member_test.cc
new file mode 100644
index 0000000..ee524b3
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_struct_member_test.cc
@@ -0,0 +1,87 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/struct_member_offset_decoration.h"
+#include "src/ast/type/i32_type.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, StructMember_Parses) {
+ auto i32 =
+ TypeManager::Instance()->Get(std::make_unique<ast::type::I32Type>());
+
+ ParserImpl p{"a : i32;"};
+ auto m = p.struct_member();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(m, nullptr);
+
+ EXPECT_EQ(m->name(), "a");
+ EXPECT_EQ(m->type(), i32);
+ EXPECT_EQ(m->decorations().size(), 0);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, StructMember_ParsesWithDecoration) {
+ auto i32 =
+ TypeManager::Instance()->Get(std::make_unique<ast::type::I32Type>());
+
+ ParserImpl p{"[[offset 2]] a : i32;"};
+ auto m = p.struct_member();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(m, nullptr);
+
+ EXPECT_EQ(m->name(), "a");
+ EXPECT_EQ(m->type(), i32);
+ EXPECT_EQ(m->decorations().size(), 1);
+ EXPECT_TRUE(m->decorations()[0]->IsOffset());
+ EXPECT_EQ(m->decorations()[0]->AsOffset()->offset(), 2);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, StructMember_InvalidDecoration) {
+ ParserImpl p{"[[offset nan]] a : i32;"};
+ auto m = p.struct_member();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(m, nullptr);
+ EXPECT_EQ(p.error(), "1:10: invalid value for offset decoration");
+}
+
+TEST_F(ParserImplTest, StructMember_InvalidVariable) {
+ ParserImpl p{"[[offset 4]] a : B;"};
+ auto m = p.struct_member();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(m, nullptr);
+ EXPECT_EQ(p.error(), "1:18: unknown type alias 'B'");
+}
+
+TEST_F(ParserImplTest, StructMember_MissingSemicolon) {
+ ParserImpl p{"a : i32"};
+ auto m = p.struct_member();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(m, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing ; for struct member");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_switch_body_test.cc b/src/reader/wgsl/parser_impl_switch_body_test.cc
new file mode 100644
index 0000000..d155ac7
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_switch_body_test.cc
@@ -0,0 +1,129 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/case_statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, SwitchBody_Case) {
+ ParserImpl p{"case 1: { a = 4; }"};
+ auto e = p.switch_body();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsCase());
+ EXPECT_FALSE(e->IsDefault());
+ ASSERT_EQ(e->body().size(), 1);
+ EXPECT_TRUE(e->body()[0]->IsAssign());
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_InvalidConstLiteral) {
+ ParserImpl p{"case a == 4: { a = 4; }"};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: unable to parse case conditional");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_MissingConstLiteral) {
+ ParserImpl p{"case: { a = 4; }"};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: unable to parse case conditional");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_MissingColon) {
+ ParserImpl p{"case 1 { a = 4; }"};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing : for case statement");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_MissingBracketLeft) {
+ ParserImpl p{"case 1: a = 4; }"};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: missing { for case statement");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_MissingBracketRight) {
+ ParserImpl p{"case 1: { a = 4; "};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:18: missing } for case statement");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Case_InvalidCaseBody) {
+ ParserImpl p{"case 1: { fn main() -> void {} }"};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing } for case statement");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Default) {
+ ParserImpl p{"default: { a = 4; }"};
+ auto e = p.switch_body();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsCase());
+ EXPECT_TRUE(e->IsDefault());
+ ASSERT_EQ(e->body().size(), 1);
+ EXPECT_TRUE(e->body()[0]->IsAssign());
+}
+
+TEST_F(ParserImplTest, SwitchBody_Default_MissingColon) {
+ ParserImpl p{"default { a = 4; }"};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: missing : for case statement");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Default_MissingBracketLeft) {
+ ParserImpl p{"default: a = 4; }"};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:10: missing { for case statement");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Default_MissingBracketRight) {
+ ParserImpl p{"default: { a = 4; "};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:19: missing } for case statement");
+}
+
+TEST_F(ParserImplTest, SwitchBody_Default_InvalidCaseBody) {
+ ParserImpl p{"default: { fn main() -> void {} }"};
+ auto e = p.switch_body();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: missing } for case statement");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_switch_stmt_test.cc b/src/reader/wgsl/parser_impl_switch_stmt_test.cc
new file mode 100644
index 0000000..8016ec5
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_switch_stmt_test.cc
@@ -0,0 +1,110 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/case_statement.h"
+#include "src/ast/switch_statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, SwitchStmt_WithoutDefault) {
+ ParserImpl p{R"(switch(a) {
+ case 1: {}
+ case 2: {}
+})"};
+ auto e = p.switch_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsSwitch());
+ ASSERT_EQ(e->body().size(), 2);
+ EXPECT_FALSE(e->body()[0]->IsDefault());
+ EXPECT_FALSE(e->body()[1]->IsDefault());
+}
+
+TEST_F(ParserImplTest, SwitchStmt_Empty) {
+ ParserImpl p{"switch(a) { }"};
+ auto e = p.switch_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsSwitch());
+ ASSERT_EQ(e->body().size(), 0);
+}
+
+TEST_F(ParserImplTest, SwitchStmt_DefaultInMiddle) {
+ ParserImpl p{R"(switch(a) {
+ case 1: {}
+ default: {}
+ case 2: {}
+})"};
+ auto e = p.switch_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsSwitch());
+
+ ASSERT_EQ(e->body().size(), 3);
+ ASSERT_FALSE(e->body()[0]->IsDefault());
+ ASSERT_TRUE(e->body()[1]->IsDefault());
+ ASSERT_FALSE(e->body()[2]->IsDefault());
+}
+
+TEST_F(ParserImplTest, SwitchStmt_InvalidExpression) {
+ ParserImpl p{"switch(a=b) {}"};
+ auto e = p.switch_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: expected )");
+}
+
+TEST_F(ParserImplTest, SwitchStmt_MissingExpression) {
+ ParserImpl p{"switch {}"};
+ auto e = p.switch_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: expected (");
+}
+
+TEST_F(ParserImplTest, SwitchStmt_MissingBracketLeft) {
+ ParserImpl p{"switch(a) }"};
+ auto e = p.switch_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing { for switch statement");
+}
+
+TEST_F(ParserImplTest, SwitchStmt_MissingBracketRight) {
+ ParserImpl p{"switch(a) {"};
+ auto e = p.switch_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: missing } for switch statement");
+}
+
+TEST_F(ParserImplTest, SwitchStmt_InvalidBody) {
+ ParserImpl p{R"(switch(a) {
+ case: {}
+})"};
+ auto e = p.switch_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "2:7: unable to parse case conditional");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_test.cc b/src/reader/wgsl/parser_impl_test.cc
new file mode 100644
index 0000000..d59d5d4
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_test.cc
@@ -0,0 +1,80 @@
+// Copyright 2020 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/reader/wgsl/parser_impl.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, Empty) {
+ ParserImpl p{""};
+ ASSERT_TRUE(p.Parse()) << p.error();
+}
+
+TEST_F(ParserImplTest, DISABLED_Parses) {
+ ParserImpl p{R"(
+import "GLSL.std.430" as glsl;
+
+[[location 0]] var<out> gl_FragColor : vec4<f32>;
+
+fn main() -> void {
+ gl_FragColor = vec4<f32>(.4, .2, .3, 1);
+}
+)"};
+ ASSERT_TRUE(p.Parse()) << p.error();
+
+ auto m = p.module();
+ ASSERT_EQ(1, m.imports().size());
+
+ // TODO(dsinclair) check rest of AST ...
+}
+
+TEST_F(ParserImplTest, DISABLED_HandlesError) {
+ ParserImpl p{R"(
+import "GLSL.std.430" as glsl;
+
+fn main() -> { # missing return type
+ return;
+})"};
+
+ ASSERT_FALSE(p.Parse());
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "4:15: missing return type for function");
+}
+
+TEST_F(ParserImplTest, GetRegisteredType) {
+ ParserImpl p{""};
+ ast::type::I32Type i32;
+ p.register_alias("my_alias", &i32);
+
+ auto alias = p.get_alias("my_alias");
+ ASSERT_NE(alias, nullptr);
+ ASSERT_EQ(alias, &i32);
+}
+
+TEST_F(ParserImplTest, GetUnregisteredType) {
+ ParserImpl p{""};
+ auto alias = p.get_alias("my_alias");
+ ASSERT_EQ(alias, nullptr);
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_type_alias_test.cc b/src/reader/wgsl/parser_impl_type_alias_test.cc
new file mode 100644
index 0000000..b4f814c
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_type_alias_test.cc
@@ -0,0 +1,96 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/type/alias_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/struct_type.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, TypeDecl_ParsesType) {
+ auto tm = TypeManager::Instance();
+ auto i32 = tm->Get(std::make_unique<ast::type::I32Type>());
+
+ ParserImpl p{"type a = i32"};
+ auto t = p.type_alias();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(t, nullptr);
+ ASSERT_TRUE(t->type()->IsI32());
+ ASSERT_EQ(t->type(), i32);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, TypeDecl_ParsesStruct) {
+ ParserImpl p{"type a = struct { b: i32; c: f32;}"};
+ auto t = p.type_alias();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(t, nullptr);
+ EXPECT_EQ(t->name(), "a");
+ ASSERT_TRUE(t->type()->IsStruct());
+
+ auto s = t->type()->AsStruct();
+ EXPECT_EQ(s->impl()->members().size(), 2);
+}
+
+TEST_F(ParserImplTest, TypeDecl_MissingIdent) {
+ ParserImpl p{"type = i32"};
+ auto t = p.type_alias();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(t, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing identifier for type alias");
+}
+
+TEST_F(ParserImplTest, TypeDecl_InvalidIdent) {
+ ParserImpl p{"type 123 = i32"};
+ auto t = p.type_alias();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(t, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing identifier for type alias");
+}
+
+TEST_F(ParserImplTest, TypeDecl_MissingEqual) {
+ ParserImpl p{"type a i32"};
+ auto t = p.type_alias();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(t, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing = for type alias");
+}
+
+TEST_F(ParserImplTest, TypeDecl_InvalidType) {
+ ParserImpl p{"type a = B"};
+ auto t = p.type_alias();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(t, nullptr);
+ EXPECT_EQ(p.error(), "1:10: unknown type alias 'B'");
+}
+
+TEST_F(ParserImplTest, TypeDecl_InvalidStruct) {
+ ParserImpl p{"type a = [[block]] {}"};
+ auto t = p.type_alias();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(t, nullptr);
+ EXPECT_EQ(p.error(), "1:20: missing struct declaration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_type_decl_test.cc b/src/reader/wgsl/parser_impl_type_decl_test.cc
new file mode 100644
index 0000000..fcf07d7
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_type_decl_test.cc
@@ -0,0 +1,506 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/type/alias_type.h"
+#include "src/ast/type/array_type.h"
+#include "src/ast/type/bool_type.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/matrix_type.h"
+#include "src/ast/type/pointer_type.h"
+#include "src/ast/type/struct_type.h"
+#include "src/ast/type/u32_type.h"
+#include "src/ast/type/vector_type.h"
+#include "src/reader/wgsl/parser_impl.h"
+#include "src/type_manager.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, TypeDecl_Invalid) {
+ ParserImpl p{"1234"};
+ auto t = p.type_decl();
+ EXPECT_EQ(t, nullptr);
+ EXPECT_FALSE(p.has_error());
+}
+
+TEST_F(ParserImplTest, TypeDecl_Identifier) {
+ ParserImpl p{"A"};
+
+ auto tm = TypeManager::Instance();
+ auto int_type = tm->Get(std::make_unique<ast::type::I32Type>());
+ // Pre-register to make sure that it's the same type.
+ auto alias_type =
+ tm->Get(std::make_unique<ast::type::AliasType>("A", int_type));
+
+ p.register_alias("A", alias_type);
+
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ EXPECT_EQ(t, alias_type);
+ ASSERT_TRUE(t->IsAlias());
+
+ auto alias = t->AsAlias();
+ EXPECT_EQ(alias->name(), "A");
+ EXPECT_EQ(alias->type(), int_type);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, TypeDecl_Identifier_NotFound) {
+ ParserImpl p{"B"};
+
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ EXPECT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:1: unknown type alias 'B'");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Bool) {
+ ParserImpl p{"bool"};
+
+ auto tm = TypeManager::Instance();
+ auto bool_type = tm->Get(std::make_unique<ast::type::BoolType>());
+
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ EXPECT_EQ(t, bool_type);
+ ASSERT_TRUE(t->IsBool());
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, TypeDecl_F32) {
+ ParserImpl p{"f32"};
+
+ auto tm = TypeManager::Instance();
+ auto float_type = tm->Get(std::make_unique<ast::type::F32Type>());
+
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ EXPECT_EQ(t, float_type);
+ ASSERT_TRUE(t->IsF32());
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, TypeDecl_I32) {
+ ParserImpl p{"i32"};
+
+ auto tm = TypeManager::Instance();
+ auto int_type = tm->Get(std::make_unique<ast::type::I32Type>());
+
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ EXPECT_EQ(t, int_type);
+ ASSERT_TRUE(t->IsI32());
+
+ TypeManager::Destroy();
+}
+
+TEST_F(ParserImplTest, TypeDecl_U32) {
+ ParserImpl p{"u32"};
+
+ auto tm = TypeManager::Instance();
+ auto uint_type = tm->Get(std::make_unique<ast::type::U32Type>());
+
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ EXPECT_EQ(t, uint_type);
+ ASSERT_TRUE(t->IsU32());
+
+ TypeManager::Destroy();
+}
+
+struct VecData {
+ const char* input;
+ size_t count;
+};
+inline std::ostream& operator<<(std::ostream& out, VecData data) {
+ out << std::string(data.input);
+ return out;
+}
+using VecTest = testing::TestWithParam<VecData>;
+TEST_P(VecTest, Parse) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ ASSERT_FALSE(p.has_error());
+ EXPECT_TRUE(t->IsVector());
+ EXPECT_EQ(t->AsVector()->size(), params.count);
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ VecTest,
+ testing::Values(VecData{"vec2<f32>", 2},
+ VecData{"vec3<f32>", 3},
+ VecData{"vec4<f32>", 4}));
+
+using VecMissingGreaterThanTest = testing::TestWithParam<VecData>;
+TEST_P(VecMissingGreaterThanTest, Handles_Missing_GreaterThan) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:9: missing > for vector");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ VecMissingGreaterThanTest,
+ testing::Values(VecData{"vec2<f32", 2},
+ VecData{"vec3<f32", 3},
+ VecData{"vec4<f32", 4}));
+
+using VecMissingLessThanTest = testing::TestWithParam<VecData>;
+TEST_P(VecMissingLessThanTest, Handles_Missing_GreaterThan) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:5: missing < for vector");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ VecMissingLessThanTest,
+ testing::Values(VecData{"vec2", 2},
+ VecData{"vec3", 3},
+ VecData{"vec4", 4}));
+
+using VecBadType = testing::TestWithParam<VecData>;
+TEST_P(VecBadType, Handles_Unknown_Type) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:6: unknown type alias 'unknown'");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ VecBadType,
+ testing::Values(VecData{"vec2<unknown", 2},
+ VecData{"vec3<unknown", 3},
+ VecData{"vec4<unknown", 4}));
+
+using VecMissingType = testing::TestWithParam<VecData>;
+TEST_P(VecMissingType, Handles_Missing_Type) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:6: unable to determine subtype for vector");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ VecMissingType,
+ testing::Values(VecData{"vec2<>", 2},
+ VecData{"vec3<>", 3},
+ VecData{"vec4<>", 4}));
+
+TEST_F(ParserImplTest, TypeDecl_Ptr) {
+ ParserImpl p{"ptr<function, f32>"};
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr) << p.error();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_TRUE(t->IsPointer());
+
+ auto ptr = t->AsPointer();
+ ASSERT_TRUE(ptr->type()->IsF32());
+ ASSERT_EQ(ptr->storage_class(), ast::StorageClass::kFunction);
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_ToVec) {
+ ParserImpl p{"ptr<function, vec2<f32>>"};
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr) << p.error();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_TRUE(t->IsPointer());
+
+ auto ptr = t->AsPointer();
+ ASSERT_TRUE(ptr->type()->IsVector());
+ ASSERT_EQ(ptr->storage_class(), ast::StorageClass::kFunction);
+
+ auto vec = ptr->type()->AsVector();
+ ASSERT_EQ(vec->size(), 2);
+ ASSERT_TRUE(vec->type()->IsF32());
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_MissingLessThan) {
+ ParserImpl p{"ptr private, f32>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:5: missing < for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_MissingGreaterThan) {
+ ParserImpl p{"ptr<function, f32"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:18: missing > for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_MissingComma) {
+ ParserImpl p{"ptr<function f32>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:14: missing , for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_MissingStorageClass) {
+ ParserImpl p{"ptr<, f32>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:5: missing storage class for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_MissingParams) {
+ ParserImpl p{"ptr<>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:5: missing storage class for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_MissingType) {
+ ParserImpl p{"ptr<function,>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:14: missing type for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_BadStorageClass) {
+ ParserImpl p{"ptr<unknown, f32>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:5: missing storage class for ptr declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Ptr_BadType) {
+ ParserImpl p{"ptr<function, unknown>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:15: unknown type alias 'unknown'");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array) {
+ ParserImpl p{"array<f32, 5>"};
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ ASSERT_FALSE(p.has_error());
+ ASSERT_TRUE(t->IsArray());
+
+ auto a = t->AsArray();
+ ASSERT_FALSE(a->IsRuntimeArray());
+ ASSERT_EQ(a->size(), 5);
+ ASSERT_TRUE(a->type()->IsF32());
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_Runtime) {
+ ParserImpl p{"array<u32>"};
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ ASSERT_FALSE(p.has_error());
+ ASSERT_TRUE(t->IsArray());
+
+ auto a = t->AsArray();
+ ASSERT_TRUE(a->IsRuntimeArray());
+ ASSERT_TRUE(a->type()->IsU32());
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_BadType) {
+ ParserImpl p{"array<unknown, 3>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:7: unknown type alias 'unknown'");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_ZeroSize) {
+ ParserImpl p{"array<f32, 0>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:12: invalid size for array declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_NegativeSize) {
+ ParserImpl p{"array<f32, -1>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:12: invalid size for array declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_BadSize) {
+ ParserImpl p{"array<f32, invalid>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:12: missing size of array declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_MissingLessThan) {
+ ParserImpl p{"array f32>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:7: missing < for array declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_MissingGreaterThan) {
+ ParserImpl p{"array<f32"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:10: missing > for array declaration");
+}
+
+TEST_F(ParserImplTest, TypeDecl_Array_MissingComma) {
+ ParserImpl p{"array<f32 3>"};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:11: missing > for array declaration");
+}
+
+struct MatrixData {
+ const char* input;
+ size_t rows;
+ size_t columns;
+};
+inline std::ostream& operator<<(std::ostream& out, MatrixData data) {
+ out << std::string(data.input);
+ return out;
+}
+using MatrixTest = testing::TestWithParam<MatrixData>;
+TEST_P(MatrixTest, Parse) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_NE(t, nullptr);
+ ASSERT_FALSE(p.has_error());
+ EXPECT_TRUE(t->IsMatrix());
+ auto mat = t->AsMatrix();
+ EXPECT_EQ(mat->rows(), params.rows);
+ EXPECT_EQ(mat->columns(), params.columns);
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ MatrixTest,
+ testing::Values(MatrixData{"mat2x2<f32>", 2, 2},
+ MatrixData{"mat2x3<f32>", 2, 3},
+ MatrixData{"mat2x4<f32>", 2, 4},
+ MatrixData{"mat3x2<f32>", 3, 2},
+ MatrixData{"mat3x3<f32>", 3, 3},
+ MatrixData{"mat3x4<f32>", 3, 4},
+ MatrixData{"mat4x2<f32>", 4, 2},
+ MatrixData{"mat4x3<f32>", 4, 3},
+ MatrixData{"mat4x4<f32>", 4, 4}));
+
+using MatrixMissingGreaterThanTest = testing::TestWithParam<MatrixData>;
+TEST_P(MatrixMissingGreaterThanTest, Handles_Missing_GreaterThan) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:11: missing > for matrix");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ MatrixMissingGreaterThanTest,
+ testing::Values(MatrixData{"mat2x2<f32", 2, 2},
+ MatrixData{"mat2x3<f32", 2, 3},
+ MatrixData{"mat2x4<f32", 2, 4},
+ MatrixData{"mat3x2<f32", 3, 2},
+ MatrixData{"mat3x3<f32", 3, 3},
+ MatrixData{"mat3x4<f32", 3, 4},
+ MatrixData{"mat4x2<f32", 4, 2},
+ MatrixData{"mat4x3<f32", 4, 3},
+ MatrixData{"mat4x4<f32", 4, 4}));
+
+using MatrixMissingLessThanTest = testing::TestWithParam<MatrixData>;
+TEST_P(MatrixMissingLessThanTest, Handles_Missing_GreaterThan) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:8: missing < for matrix");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ MatrixMissingLessThanTest,
+ testing::Values(MatrixData{"mat2x2 f32>", 2, 2},
+ MatrixData{"mat2x3 f32>", 2, 3},
+ MatrixData{"mat2x4 f32>", 2, 4},
+ MatrixData{"mat3x2 f32>", 3, 2},
+ MatrixData{"mat3x3 f32>", 3, 3},
+ MatrixData{"mat3x4 f32>", 3, 4},
+ MatrixData{"mat4x2 f32>", 4, 2},
+ MatrixData{"mat4x3 f32>", 4, 3},
+ MatrixData{"mat4x4 f32>", 4, 4}));
+
+using MatrixBadType = testing::TestWithParam<MatrixData>;
+TEST_P(MatrixBadType, Handles_Unknown_Type) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:8: unknown type alias 'unknown'");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ MatrixBadType,
+ testing::Values(MatrixData{"mat2x2<unknown>", 2, 2},
+ MatrixData{"mat2x3<unknown>", 2, 3},
+ MatrixData{"mat2x4<unknown>", 2, 4},
+ MatrixData{"mat3x2<unknown>", 3, 2},
+ MatrixData{"mat3x3<unknown>", 3, 3},
+ MatrixData{"mat3x4<unknown>", 3, 4},
+ MatrixData{"mat4x2<unknown>", 4, 2},
+ MatrixData{"mat4x3<unknown>", 4, 3},
+ MatrixData{"mat4x4<unknown>", 4, 4}));
+
+using MatrixMissingType = testing::TestWithParam<MatrixData>;
+TEST_P(MatrixMissingType, Handles_Missing_Type) {
+ auto params = GetParam();
+ ParserImpl p{params.input};
+ auto t = p.type_decl();
+ ASSERT_EQ(t, nullptr);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:8: unable to determine subtype for matrix");
+}
+INSTANTIATE_TEST_SUITE_P(ParserImplTest,
+ MatrixMissingType,
+ testing::Values(MatrixData{"mat2x2<>", 2, 2},
+ MatrixData{"mat2x3<>", 2, 3},
+ MatrixData{"mat2x4<>", 2, 4},
+ MatrixData{"mat3x2<>", 3, 2},
+ MatrixData{"mat3x3<>", 3, 3},
+ MatrixData{"mat3x4<>", 3, 4},
+ MatrixData{"mat4x2<>", 4, 2},
+ MatrixData{"mat4x3<>", 4, 3},
+ MatrixData{"mat4x4<>", 4, 4}));
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_unary_expression_test.cc b/src/reader/wgsl/parser_impl_unary_expression_test.cc
new file mode 100644
index 0000000..48acd55
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_unary_expression_test.cc
@@ -0,0 +1,846 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/const_initializer_expression.h"
+#include "src/ast/identifier_expression.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/unary_derivative_expression.h"
+#include "src/ast/unary_method_expression.h"
+#include "src/ast/unary_op_expression.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, UnaryExpression_Postix) {
+ ParserImpl p{"a[2]"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+
+ ASSERT_TRUE(e->IsArrayAccessor());
+ auto ary = e->AsArrayAccessor();
+ ASSERT_TRUE(ary->array()->IsIdentifier());
+ auto ident = ary->array()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(ary->idx_expr()->IsInitializer());
+ ASSERT_TRUE(ary->idx_expr()->AsInitializer()->IsConstInitializer());
+ auto init = ary->idx_expr()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsInt());
+ ASSERT_EQ(init->literal()->AsInt()->value(), 2);
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Minus) {
+ ParserImpl p{"- 1"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryOp());
+
+ auto u = e->AsUnaryOp();
+ ASSERT_EQ(u->op(), ast::UnaryOp::kNegation);
+
+ ASSERT_TRUE(u->expr()->IsInitializer());
+ ASSERT_TRUE(u->expr()->AsInitializer()->IsConstInitializer());
+
+ auto init = u->expr()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsInt());
+ EXPECT_EQ(init->literal()->AsInt()->value(), 1);
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Minus_InvalidRHS) {
+ ParserImpl p{"-if(a) {}"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:2: unable to parse right side of - expression");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Bang) {
+ ParserImpl p{"!1"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryOp());
+
+ auto u = e->AsUnaryOp();
+ ASSERT_EQ(u->op(), ast::UnaryOp::kNot);
+
+ ASSERT_TRUE(u->expr()->IsInitializer());
+ ASSERT_TRUE(u->expr()->AsInitializer()->IsConstInitializer());
+
+ auto init = u->expr()->AsInitializer()->AsConstInitializer();
+ ASSERT_TRUE(init->literal()->IsInt());
+ EXPECT_EQ(init->literal()->AsInt()->value(), 1);
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Bang_InvalidRHS) {
+ ParserImpl p{"!if (a) {}"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:2: unable to parse right side of ! expression");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Any) {
+ ParserImpl p{"any(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryMethod());
+
+ auto u = e->AsUnaryMethod();
+ ASSERT_EQ(u->op(), ast::UnaryMethod::kAny);
+ ASSERT_EQ(u->params().size(), 1);
+ ASSERT_TRUE(u->params()[0]->IsIdentifier());
+ auto ident = u->params()[0]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Any_MissingParenLeft) {
+ ParserImpl p{"any a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing ( for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Any_MissingParenRight) {
+ ParserImpl p{"any(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing ) for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Any_MissingIdentifier) {
+ ParserImpl p{"any()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Any_InvalidIdentifier) {
+ ParserImpl p{"any(123)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_All) {
+ ParserImpl p{"all(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryMethod());
+
+ auto u = e->AsUnaryMethod();
+ ASSERT_EQ(u->op(), ast::UnaryMethod::kAll);
+ ASSERT_EQ(u->params().size(), 1);
+ ASSERT_TRUE(u->params()[0]->IsIdentifier());
+ auto ident = u->params()[0]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_All_MissingParenLeft) {
+ ParserImpl p{"all a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing ( for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_All_MissingParenRight) {
+ ParserImpl p{"all(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing ) for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_All_MissingIdentifier) {
+ ParserImpl p{"all()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_All_InvalidIdentifier) {
+ ParserImpl p{"all(123)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNan) {
+ ParserImpl p{"is_nan(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryMethod());
+
+ auto u = e->AsUnaryMethod();
+ ASSERT_EQ(u->op(), ast::UnaryMethod::kIsNan);
+ ASSERT_EQ(u->params().size(), 1);
+ ASSERT_TRUE(u->params()[0]->IsIdentifier());
+ auto ident = u->params()[0]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNan_MissingParenLeft) {
+ ParserImpl p{"is_nan a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing ( for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNan_MissingParenRight) {
+ ParserImpl p{"is_nan(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: missing ) for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNan_MissingIdentifier) {
+ ParserImpl p{"is_nan()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNan_InvalidIdentifier) {
+ ParserImpl p{"is_nan(123)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsInf) {
+ ParserImpl p{"is_inf(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryMethod());
+
+ auto u = e->AsUnaryMethod();
+ ASSERT_EQ(u->op(), ast::UnaryMethod::kIsInf);
+ ASSERT_EQ(u->params().size(), 1);
+ ASSERT_TRUE(u->params()[0]->IsIdentifier());
+ auto ident = u->params()[0]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsInf_MissingParenLeft) {
+ ParserImpl p{"is_inf a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing ( for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsInf_MissingParenRight) {
+ ParserImpl p{"is_inf(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: missing ) for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsInf_MissingIdentifier) {
+ ParserImpl p{"is_inf()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsInf_InvalidIdentifier) {
+ ParserImpl p{"is_inf(123)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsFinite) {
+ ParserImpl p{"is_finite(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryMethod());
+
+ auto u = e->AsUnaryMethod();
+ ASSERT_EQ(u->op(), ast::UnaryMethod::kIsFinite);
+ ASSERT_EQ(u->params().size(), 1);
+ ASSERT_TRUE(u->params()[0]->IsIdentifier());
+ auto ident = u->params()[0]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsFinite_MissingParenLeft) {
+ ParserImpl p{"is_finite a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing ( for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsFinite_MissingParenRight) {
+ ParserImpl p{"is_finite(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: missing ) for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsFinite_MissingIdentifier) {
+ ParserImpl p{"is_finite()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsFinite_InvalidIdentifier) {
+ ParserImpl p{"is_finite(123)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNormal) {
+ ParserImpl p{"is_normal(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryMethod());
+
+ auto u = e->AsUnaryMethod();
+ ASSERT_EQ(u->op(), ast::UnaryMethod::kIsNormal);
+ ASSERT_EQ(u->params().size(), 1);
+ ASSERT_TRUE(u->params()[0]->IsIdentifier());
+ auto ident = u->params()[0]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNormal_MissingParenLeft) {
+ ParserImpl p{"is_normal a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing ( for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNormal_MissingParenRight) {
+ ParserImpl p{"is_normal(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:12: missing ) for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNormal_MissingIdentifier) {
+ ParserImpl p{"is_normal()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_IsNormal_InvalidIdentifier) {
+ ParserImpl p{"is_normal(123)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dot) {
+ ParserImpl p{"dot(a, b)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryMethod());
+
+ auto u = e->AsUnaryMethod();
+ ASSERT_EQ(u->op(), ast::UnaryMethod::kDot);
+ ASSERT_EQ(u->params().size(), 2);
+ ASSERT_TRUE(u->params()[0]->IsIdentifier());
+ auto ident = u->params()[0]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(u->params()[1]->IsIdentifier());
+ ident = u->params()[1]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "b");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dot_MissingParenLeft) {
+ ParserImpl p{"dot a, b)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing ( for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dot_MissingParenRight) {
+ ParserImpl p{"dot(a, b"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: missing ) for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dot_MissingFirstIdentifier) {
+ ParserImpl p{"dot(, a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dot_MissingSecondIdentifier) {
+ ParserImpl p{"dot(a, )"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dot_MissingComma) {
+ ParserImpl p{"dot(a b)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:7: missing , for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dot_InvalidFirstIdentifier) {
+ ParserImpl p{"dot(123, b)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:5: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dot_InvalidSecondIdentifier) {
+ ParserImpl p{"dot(a, 123)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_OuterProduct) {
+ ParserImpl p{"outer_product(a, b)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryMethod());
+
+ auto u = e->AsUnaryMethod();
+ ASSERT_EQ(u->op(), ast::UnaryMethod::kOuterProduct);
+ ASSERT_EQ(u->params().size(), 2);
+ ASSERT_TRUE(u->params()[0]->IsIdentifier());
+ auto ident = u->params()[0]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+
+ ASSERT_TRUE(u->params()[1]->IsIdentifier());
+ ident = u->params()[1]->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "b");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingParenLeft) {
+ ParserImpl p{"outer_product a, b)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing ( for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingParenRight) {
+ ParserImpl p{"outer_product(a, b"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:19: missing ) for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingFirstIdentifier) {
+ ParserImpl p{"outer_product(, b)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingSecondIdentifier) {
+ ParserImpl p{"outer_product(a, )"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:18: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_OuterProduct_MissingComma) {
+ ParserImpl p{"outer_product(a b)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:17: missing , for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_OuterProduct_InvalidFirstIdentifier) {
+ ParserImpl p{"outer_product(123, b)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_OuterProduct_InvalidSecondIdentifier) {
+ ParserImpl p{"outer_product(a, 123)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:18: missing identifier for method call");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_NoModifier) {
+ ParserImpl p{"dpdx(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryDerivative());
+
+ auto deriv = e->AsUnaryDerivative();
+ EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kDpdx);
+ EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kNone);
+
+ ASSERT_NE(deriv->param(), nullptr);
+ ASSERT_TRUE(deriv->param()->IsIdentifier());
+ auto ident = deriv->param()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_WithModifier) {
+ ParserImpl p{"dpdx<coarse>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryDerivative());
+
+ auto deriv = e->AsUnaryDerivative();
+ EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kDpdx);
+ EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kCoarse);
+
+ ASSERT_NE(deriv->param(), nullptr);
+ ASSERT_TRUE(deriv->param()->IsIdentifier());
+ auto ident = deriv->param()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_MissingLessThan) {
+ ParserImpl p{"dpdx coarse>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_InvalidModifier) {
+ ParserImpl p{"dpdx<invalid>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: unable to parse derivative modifier");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_EmptyModifer) {
+ ParserImpl p{"dpdx coarse>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_MissingGreaterThan) {
+ ParserImpl p{"dpdx<coarse (a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: missing > for derivative modifier");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_MisisngLeftParen) {
+ ParserImpl p{"dpdx<coarse>a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_MissingRightParen) {
+ ParserImpl p{"dpdx<coarse>(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing ) for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_MissingIdentifier) {
+ ParserImpl p{"dpdx<coarse>()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: missing identifier for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdx_InvalidIdentifeir) {
+ ParserImpl p{"dpdx<coarse>(12345)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: missing identifier for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_NoModifier) {
+ ParserImpl p{"dpdy(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryDerivative());
+
+ auto deriv = e->AsUnaryDerivative();
+ EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kDpdy);
+ EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kNone);
+
+ ASSERT_NE(deriv->param(), nullptr);
+ ASSERT_TRUE(deriv->param()->IsIdentifier());
+ auto ident = deriv->param()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_WithModifier) {
+ ParserImpl p{"dpdy<fine>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryDerivative());
+
+ auto deriv = e->AsUnaryDerivative();
+ EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kDpdy);
+ EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kFine);
+
+ ASSERT_NE(deriv->param(), nullptr);
+ ASSERT_TRUE(deriv->param()->IsIdentifier());
+ auto ident = deriv->param()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_MissingLessThan) {
+ ParserImpl p{"dpdy coarse>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_InvalidModifier) {
+ ParserImpl p{"dpdy<invalid>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: unable to parse derivative modifier");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_EmptyModifer) {
+ ParserImpl p{"dpdy coarse>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:6: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_MissingGreaterThan) {
+ ParserImpl p{"dpdy<coarse (a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: missing > for derivative modifier");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_MisisngLeftParen) {
+ ParserImpl p{"dpdy<coarse>a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:13: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_MissingRightParen) {
+ ParserImpl p{"dpdy<coarse>(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing ) for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_MissingIdentifier) {
+ ParserImpl p{"dpdy<coarse>()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: missing identifier for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Dpdy_InvalidIdentifeir) {
+ ParserImpl p{"dpdy<coarse>(12345)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:14: missing identifier for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_NoModifier) {
+ ParserImpl p{"fwidth(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryDerivative());
+
+ auto deriv = e->AsUnaryDerivative();
+ EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kFwidth);
+ EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kNone);
+
+ ASSERT_NE(deriv->param(), nullptr);
+ ASSERT_TRUE(deriv->param()->IsIdentifier());
+ auto ident = deriv->param()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_WithModifier) {
+ ParserImpl p{"fwidth<coarse>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnaryDerivative());
+
+ auto deriv = e->AsUnaryDerivative();
+ EXPECT_EQ(deriv->op(), ast::UnaryDerivative::kFwidth);
+ EXPECT_EQ(deriv->modifier(), ast::DerivativeModifier::kCoarse);
+
+ ASSERT_NE(deriv->param(), nullptr);
+ ASSERT_TRUE(deriv->param()->IsIdentifier());
+ auto ident = deriv->param()->AsIdentifier();
+ ASSERT_EQ(ident->name().size(), 1);
+ EXPECT_EQ(ident->name()[0], "a");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_MissingLessThan) {
+ ParserImpl p{"fwidth coarse>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_InvalidModifier) {
+ ParserImpl p{"fwidth<invalid>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse derivative modifier");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_EmptyModifer) {
+ ParserImpl p{"fwidth coarse>(a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_MissingGreaterThan) {
+ ParserImpl p{"fwidth<coarse (a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing > for derivative modifier");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_MisisngLeftParen) {
+ ParserImpl p{"fwidth<coarse>a)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing ( for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_MissingRightParen) {
+ ParserImpl p{"fwidth<coarse>(a"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:17: missing ) for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidth_MissingIdentifier) {
+ ParserImpl p{"fwidth<coarse>()"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:16: missing identifier for derivative method");
+}
+
+TEST_F(ParserImplTest, UnaryExpression_Fwidht_InvalidIdentifeir) {
+ ParserImpl p{"fwidth<coarse>(12345)"};
+ auto e = p.unary_expression();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:16: missing identifier for derivative method");
+}
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_unless_stmt_test.cc b/src/reader/wgsl/parser_impl_unless_stmt_test.cc
new file mode 100644
index 0000000..f34d7d8
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_unless_stmt_test.cc
@@ -0,0 +1,62 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, UnlessStmt) {
+ ParserImpl p{"unless (a) { kill; }"};
+ auto e = p.unless_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsUnless());
+ ASSERT_NE(e->condition(), nullptr);
+ EXPECT_TRUE(e->condition()->IsIdentifier());
+ ASSERT_EQ(e->body().size(), 1);
+ EXPECT_TRUE(e->body()[0]->IsKill());
+}
+
+TEST_F(ParserImplTest, UnlessStmt_InvalidCondition) {
+ ParserImpl p{"unless(if(a){}) {}"};
+ auto e = p.unless_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, UnlessStmt_EmptyCondition) {
+ ParserImpl p{"unless() {}"};
+ auto e = p.unless_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:8: unable to parse expression");
+}
+
+TEST_F(ParserImplTest, UnlessStmt_InvalidBody) {
+ ParserImpl p{"unless(a + 2 - 5 == true) { kill }"};
+ auto e = p.unless_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:34: missing ;");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_variable_decl_test.cc b/src/reader/wgsl/parser_impl_variable_decl_test.cc
new file mode 100644
index 0000000..73d7c17
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -0,0 +1,75 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/variable.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, VariableDecl_Parses) {
+ ParserImpl p{"var my_var : f32"};
+ auto var = p.variable_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(var, nullptr);
+ ASSERT_EQ(var->name(), "my_var");
+ ASSERT_NE(var->type(), nullptr);
+ ASSERT_EQ(var->source().line, 1);
+ ASSERT_EQ(var->source().column, 1);
+ ASSERT_TRUE(var->type()->IsF32());
+}
+
+TEST_F(ParserImplTest, VariableDecl_MissingVar) {
+ ParserImpl p{"my_var : f32"};
+ auto v = p.variable_decl();
+ ASSERT_EQ(v, nullptr);
+ ASSERT_FALSE(p.has_error());
+
+ auto t = p.next();
+ ASSERT_TRUE(t.IsIdentifier());
+}
+
+TEST_F(ParserImplTest, VariableDecl_InvalidIdentDecl) {
+ ParserImpl p{"var my_var f32"};
+ auto v = p.variable_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(v, nullptr);
+ ASSERT_EQ(p.error(), "1:12: missing : for identifier declaration");
+}
+
+TEST_F(ParserImplTest, VariableDecl_WithStorageClass) {
+ ParserImpl p{"var<private> my_var : f32"};
+ auto v = p.variable_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(v, nullptr);
+ EXPECT_EQ(v->name(), "my_var");
+ EXPECT_TRUE(v->type()->IsF32());
+ EXPECT_EQ(v->storage_class(), ast::StorageClass::kPrivate);
+}
+
+TEST_F(ParserImplTest, VariableDecl_InvalidStorageClass) {
+ ParserImpl p{"var<unknown> my_var : f32"};
+ auto v = p.variable_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(v, nullptr);
+ EXPECT_EQ(p.error(), "1:5: invalid storage class for variable decoration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_variable_decoration_list_test.cc b/src/reader/wgsl/parser_impl_variable_decoration_list_test.cc
new file mode 100644
index 0000000..19029e0
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_variable_decoration_list_test.cc
@@ -0,0 +1,81 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/builtin_decoration.h"
+#include "src/ast/location_decoration.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, VariableDecorationList_Parses) {
+ ParserImpl p{R"([[location 4, builtin position]])"};
+ auto decos = p.variable_decoration_list();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_EQ(decos.size(), 2);
+ ASSERT_TRUE(decos[0]->IsLocation());
+ EXPECT_EQ(decos[0]->AsLocation()->value(), 4);
+ ASSERT_TRUE(decos[1]->IsBuiltin());
+ EXPECT_EQ(decos[1]->AsBuiltin()->value(), ast::Builtin::kPosition);
+}
+
+TEST_F(ParserImplTest, VariableDecorationList_Empty) {
+ ParserImpl p{R"([[]])"};
+ auto decos = p.variable_decoration_list();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:3: empty variable decoration list");
+}
+
+TEST_F(ParserImplTest, VariableDecorationList_Invalid) {
+ ParserImpl p{R"([[invalid]])"};
+ auto decos = p.variable_decoration_list();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:3: missing variable decoration for decoration list");
+}
+
+TEST_F(ParserImplTest, VariableDecorationList_ExtraComma) {
+ ParserImpl p{R"([[builtin position, ]])"};
+ auto decos = p.variable_decoration_list();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:21: missing variable decoration after comma");
+}
+
+TEST_F(ParserImplTest, VariableDecorationList_MissingComma) {
+ ParserImpl p{R"([[binding 4 location 5]])"};
+ auto decos = p.variable_decoration_list();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:13: missing comma in variable decoration list");
+}
+
+TEST_F(ParserImplTest, VariableDecorationList_BadDecoration) {
+ ParserImpl p{R"([[location bad]])"};
+ auto decos = p.variable_decoration_list();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:12: invalid value for location decoration");
+}
+
+TEST_F(ParserImplTest, VariableDecorationList_InvalidBuiltin) {
+ ParserImpl p{"[[builtin invalid]]"};
+ auto decos = p.variable_decoration_list();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:11: invalid value for builtin decoration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_variable_decoration_test.cc b/src/reader/wgsl/parser_impl_variable_decoration_test.cc
new file mode 100644
index 0000000..311dd5a
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_variable_decoration_test.cc
@@ -0,0 +1,138 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/binding_decoration.h"
+#include "src/ast/builtin_decoration.h"
+#include "src/ast/location_decoration.h"
+#include "src/ast/set_decoration.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, VariableDecoration_Location) {
+ ParserImpl p{"location 4"};
+ auto deco = p.variable_decoration();
+ ASSERT_NE(deco, nullptr);
+ ASSERT_FALSE(p.has_error());
+ ASSERT_TRUE(deco->IsLocation());
+
+ auto loc = deco->AsLocation();
+ EXPECT_EQ(loc->value(), 4);
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Location_MissingValue) {
+ ParserImpl p{"location"};
+ auto deco = p.variable_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:9: invalid value for location decoration");
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Location_MissingInvalid) {
+ ParserImpl p{"location nan"};
+ auto deco = p.variable_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:10: invalid value for location decoration");
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Builtin) {
+ ParserImpl p{"builtin frag_depth"};
+ auto deco = p.variable_decoration();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(deco, nullptr);
+ ASSERT_TRUE(deco->IsBuiltin());
+
+ auto builtin = deco->AsBuiltin();
+ EXPECT_EQ(builtin->value(), ast::Builtin::kFragDepth);
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Builtin_MissingValue) {
+ ParserImpl p{"builtin"};
+ auto deco = p.variable_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:8: invalid value for builtin decoration");
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Builtin_MissingInvalid) {
+ ParserImpl p{"builtin 3"};
+ auto deco = p.variable_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:9: invalid value for builtin decoration");
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Binding) {
+ ParserImpl p{"binding 4"};
+ auto deco = p.variable_decoration();
+ ASSERT_NE(deco, nullptr);
+ ASSERT_FALSE(p.has_error());
+ ASSERT_TRUE(deco->IsBinding());
+
+ auto binding = deco->AsBinding();
+ EXPECT_EQ(binding->value(), 4);
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Binding_MissingValue) {
+ ParserImpl p{"binding"};
+ auto deco = p.variable_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:8: invalid value for binding decoration");
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Binding_MissingInvalid) {
+ ParserImpl p{"binding nan"};
+ auto deco = p.variable_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:9: invalid value for binding decoration");
+}
+
+TEST_F(ParserImplTest, VariableDecoration_set) {
+ ParserImpl p{"set 4"};
+ auto deco = p.variable_decoration();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_NE(deco.get(), nullptr);
+ ASSERT_TRUE(deco->IsSet());
+
+ auto set = deco->AsSet();
+ EXPECT_EQ(set->value(), 4);
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Set_MissingValue) {
+ ParserImpl p{"set"};
+ auto deco = p.variable_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:4: invalid value for set decoration");
+}
+
+TEST_F(ParserImplTest, VariableDecoration_Set_MissingInvalid) {
+ ParserImpl p{"set nan"};
+ auto deco = p.variable_decoration();
+ ASSERT_EQ(deco, nullptr);
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "1:5: invalid value for set decoration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
new file mode 100644
index 0000000..393884b
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc
@@ -0,0 +1,84 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, VariableIdentDecl_Parses) {
+ ParserImpl p{"my_var : f32"};
+ std::string name;
+ ast::type::Type* type;
+ std::tie(name, type) = p.variable_ident_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_EQ(name, "my_var");
+ ASSERT_NE(type, nullptr);
+ ASSERT_TRUE(type->IsF32());
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_MissingIdent) {
+ ParserImpl p{": f32"};
+ std::string name;
+ ast::type::Type* type;
+ std::tie(name, type) = p.variable_ident_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_EQ(name, "");
+ ASSERT_EQ(type, nullptr);
+
+ auto t = p.next();
+ ASSERT_TRUE(t.IsColon());
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_MissingColon) {
+ ParserImpl p{"my_var f32"};
+ auto r = p.variable_ident_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:8: missing : for identifier declaration");
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_MissingType) {
+ ParserImpl p{"my_var :"};
+ auto r = p.variable_ident_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:9: invalid type for identifier declaration");
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_InvalidIdent) {
+ ParserImpl p{"123 : f32"};
+ std::string name;
+ ast::type::Type* type;
+ std::tie(name, type) = p.variable_ident_decl();
+ ASSERT_FALSE(p.has_error());
+ ASSERT_EQ(name, "");
+ ASSERT_EQ(type, nullptr);
+
+ auto t = p.next();
+ ASSERT_TRUE(t.IsIntLiteral());
+}
+
+TEST_F(ParserImplTest, VariableIdentDecl_InvalidType) {
+ ParserImpl p{"my_var : invalid"};
+ auto r = p.variable_ident_decl();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:10: unknown type alias 'invalid'");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_variable_stmt_test.cc b/src/reader/wgsl/parser_impl_variable_stmt_test.cc
new file mode 100644
index 0000000..bbbfdc6
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_variable_stmt_test.cc
@@ -0,0 +1,109 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/statement.h"
+#include "src/ast/variable_statement.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+TEST_F(ParserImplTest, VariableStmt_VariableDecl) {
+ ParserImpl p{"var a : i32;"};
+ auto e = p.variable_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsVariable());
+ ASSERT_NE(e->variable(), nullptr);
+ EXPECT_EQ(e->variable()->name(), "a");
+
+ EXPECT_EQ(e->variable()->initializer(), nullptr);
+}
+
+TEST_F(ParserImplTest, VariableStmt_VariableDecl_WithInit) {
+ ParserImpl p{"var a : i32 = 1;"};
+ auto e = p.variable_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsVariable());
+ ASSERT_NE(e->variable(), nullptr);
+ EXPECT_EQ(e->variable()->name(), "a");
+
+ ASSERT_NE(e->variable()->initializer(), nullptr);
+ EXPECT_TRUE(e->variable()->initializer()->IsInitializer());
+}
+
+TEST_F(ParserImplTest, VariableStmt_VariableDecl_Invalid) {
+ ParserImpl p{"var a : invalid;"};
+ auto e = p.variable_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:9: unknown type alias 'invalid'");
+}
+
+TEST_F(ParserImplTest, VariableStmt_VariableDecl_InitializerInvalid) {
+ ParserImpl p{"var a : i32 = if(a) {}"};
+ auto e = p.variable_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing initializer for variable declaration");
+}
+
+TEST_F(ParserImplTest, VariableStmt_Const) {
+ ParserImpl p{"const a : i32 = 1"};
+ auto e = p.variable_stmt();
+ ASSERT_FALSE(p.has_error()) << p.error();
+ ASSERT_NE(e, nullptr);
+ ASSERT_TRUE(e->IsVariable());
+}
+
+TEST_F(ParserImplTest, VariableStmt_Const_InvalidVarIdent) {
+ ParserImpl p{"const a : invalid = 1"};
+ auto e = p.variable_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:11: unknown type alias 'invalid'");
+}
+
+TEST_F(ParserImplTest, VariableStmt_Const_MissingEqual) {
+ ParserImpl p{"const a : i32 1"};
+ auto e = p.variable_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:15: missing = for constant declaration");
+}
+
+TEST_F(ParserImplTest, VariableStmt_Const_MissingInitializer) {
+ ParserImpl p{"const a : i32 ="};
+ auto e = p.variable_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:16: missing initializer for const declaration");
+}
+
+TEST_F(ParserImplTest, VariableStmt_Const_InvalidInitializer) {
+ ParserImpl p{"const a : i32 = if (a) {}"};
+ auto e = p.variable_stmt();
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(e, nullptr);
+ EXPECT_EQ(p.error(), "1:17: missing initializer for const declaration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_impl_variable_storage_decoration_test.cc b/src/reader/wgsl/parser_impl_variable_storage_decoration_test.cc
new file mode 100644
index 0000000..04963d7
--- /dev/null
+++ b/src/reader/wgsl/parser_impl_variable_storage_decoration_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2020 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 "gtest/gtest.h"
+#include "src/ast/storage_class.h"
+#include "src/reader/wgsl/parser_impl.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserImplTest = testing::Test;
+
+struct VariableStorageData {
+ const char* input;
+ ast::StorageClass result;
+};
+inline std::ostream& operator<<(std::ostream& out, VariableStorageData data) {
+ out << std::string(data.input);
+ return out;
+}
+using VariableStorageTest = testing::TestWithParam<VariableStorageData>;
+TEST_P(VariableStorageTest, Parses) {
+ auto params = GetParam();
+ ParserImpl p{std::string("<") + params.input + ">"};
+
+ auto sc = p.variable_storage_decoration();
+ ASSERT_FALSE(p.has_error());
+ EXPECT_EQ(sc, params.result);
+
+ auto t = p.next();
+ EXPECT_TRUE(t.IsEof());
+}
+INSTANTIATE_TEST_SUITE_P(
+ ParserImplTest,
+ VariableStorageTest,
+ testing::Values(
+ VariableStorageData{"in", ast::StorageClass::kInput},
+ VariableStorageData{"out", ast::StorageClass::kOutput},
+ VariableStorageData{"uniform", ast::StorageClass::kUniform},
+ VariableStorageData{"workgroup", ast::StorageClass::kWorkgroup},
+ VariableStorageData{"uniform_constant",
+ ast::StorageClass::kUniformConstant},
+ VariableStorageData{"storage_buffer",
+ ast::StorageClass::kStorageBuffer},
+ VariableStorageData{"image", ast::StorageClass::kImage},
+ VariableStorageData{"push_constant", ast::StorageClass::kPushConstant},
+ VariableStorageData{"private", ast::StorageClass::kPrivate},
+ VariableStorageData{"function", ast::StorageClass::kFunction}));
+
+TEST_F(ParserImplTest, VariableStorageDecoration_NoMatch) {
+ ParserImpl p{"<not-a-storage-class>"};
+ auto sc = p.variable_storage_decoration();
+ ASSERT_EQ(sc, ast::StorageClass::kNone);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:2: invalid storage class for variable decoration");
+}
+
+TEST_F(ParserImplTest, VariableStorageDecoration_Empty) {
+ ParserImpl p{"<>"};
+ auto sc = p.variable_storage_decoration();
+ ASSERT_EQ(sc, ast::StorageClass::kNone);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:2: invalid storage class for variable decoration");
+}
+
+TEST_F(ParserImplTest, VariableStorageDecoration_MissingLessThan) {
+ ParserImpl p{"in>"};
+ auto sc = p.variable_storage_decoration();
+ ASSERT_EQ(sc, ast::StorageClass::kNone);
+ ASSERT_FALSE(p.has_error());
+
+ auto t = p.next();
+ ASSERT_TRUE(t.IsIn());
+}
+
+TEST_F(ParserImplTest, VariableStorageDecoration_MissingGreaterThan) {
+ ParserImpl p{"<in"};
+ auto sc = p.variable_storage_decoration();
+ ASSERT_EQ(sc, ast::StorageClass::kNone);
+ ASSERT_TRUE(p.has_error());
+ ASSERT_EQ(p.error(), "1:4: missing > for variable decoration");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/parser_test.cc b/src/reader/wgsl/parser_test.cc
new file mode 100644
index 0000000..6ef3016
--- /dev/null
+++ b/src/reader/wgsl/parser_test.cc
@@ -0,0 +1,63 @@
+// Copyright 2020 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/reader/wgsl/parser.h"
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using ParserTest = testing::Test;
+
+TEST_F(ParserTest, Empty) {
+ Parser p{""};
+ ASSERT_TRUE(p.Parse()) << p.error();
+}
+
+TEST_F(ParserTest, DISABLED_Parses) {
+ Parser p{R"(
+import "GLSL.std.430" as glsl;
+
+[[location 0]] var<out> gl_FragColor : vec4<f32>;
+
+fn main() -> void {
+ gl_FragColor = vec4<f32>(.4, .2, .3, 1);
+}
+)"};
+ ASSERT_TRUE(p.Parse()) << p.error();
+
+ auto m = p.module();
+ ASSERT_EQ(1, m.imports().size());
+
+ // TODO(dsinclair) check rest of AST ...
+}
+
+TEST_F(ParserTest, DISABLED_HandlesError) {
+ Parser p{R"(
+import "GLSL.std.430" as glsl;
+
+fn main() -> { # missing return type
+ return;
+})"};
+
+ ASSERT_FALSE(p.Parse());
+ ASSERT_TRUE(p.has_error());
+ EXPECT_EQ(p.error(), "4:15: missing return type for function");
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
new file mode 100644
index 0000000..fbbc1aa
--- /dev/null
+++ b/src/reader/wgsl/token.cc
@@ -0,0 +1,344 @@
+// Copyright 2020 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/reader/wgsl/token.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+// static
+std::string Token::TypeToName(Type type) {
+ switch (type) {
+ case Token::Type::kError:
+ return "kError";
+ case Token::Type::kReservedKeyword:
+ return "kReservedKeyword";
+ case Token::Type::kEOF:
+ return "kEOF";
+ case Token::Type::kIdentifier:
+ return "kIdentifier";
+ case Token::Type::kStringLiteral:
+ return "kStringLiteral";
+ case Token::Type::kFloatLiteral:
+ return "kFloatLiteral";
+ case Token::Type::kIntLiteral:
+ return "kIntLiteral";
+ case Token::Type::kUintLiteral:
+ return "kUintLiteral";
+ case Token::Type::kUninitialized:
+ return "kUninitialized";
+
+ case Token::Type::kAnd:
+ return "&";
+ case Token::Type::kAndAnd:
+ return "&&";
+ case Token::Type::kArrow:
+ return "->";
+ case Token::Type::kAttrLeft:
+ return "[[";
+ case Token::Type::kAttrRight:
+ return "]]";
+ case Token::Type::kForwardSlash:
+ return "/";
+ case Token::Type::kBang:
+ return "!";
+ case Token::Type::kBraceLeft:
+ return "[";
+ case Token::Type::kBraceRight:
+ return "]";
+ case Token::Type::kBracketLeft:
+ return "{";
+ case Token::Type::kBracketRight:
+ return "}";
+ case Token::Type::kColon:
+ return ":";
+ case Token::Type::kComma:
+ return ",";
+ case Token::Type::kEqual:
+ return "=";
+ case Token::Type::kEqualEqual:
+ return "==";
+ case Token::Type::kGreaterThan:
+ return ">";
+ case Token::Type::kGreaterThanEqual:
+ return ">=";
+ case Token::Type::kLessThan:
+ return "<";
+ case Token::Type::kLessThanEqual:
+ return "<=";
+ case Token::Type::kMod:
+ return "%";
+ case Token::Type::kNotEqual:
+ return "!=";
+ case Token::Type::kMinus:
+ return "-";
+ case Token::Type::kNamespace:
+ return "::";
+ case Token::Type::kPeriod:
+ return ".";
+ case Token::Type::kPlus:
+ return "+";
+ case Token::Type::kOr:
+ return "|";
+ case Token::Type::kOrOr:
+ return "||";
+ case Token::Type::kParenLeft:
+ return "(";
+ case Token::Type::kParenRight:
+ return ")";
+ case Token::Type::kSemicolon:
+ return ";";
+ case Token::Type::kStar:
+ return "*";
+ case Token::Type::kXor:
+ return "^";
+
+ case Token::Type::kAll:
+ return "all";
+ case Token::Type::kAny:
+ return "any";
+ case Token::Type::kArray:
+ return "array";
+ case Token::Type::kAs:
+ return "as";
+ case Token::Type::kBinding:
+ return "binding";
+ case Token::Type::kBlock:
+ return "block";
+ case Token::Type::kBool:
+ return "bool";
+ case Token::Type::kBreak:
+ return "break";
+ case Token::Type::kBuiltin:
+ return "builtin";
+ case Token::Type::kCase:
+ return "case";
+ case Token::Type::kCast:
+ return "cast";
+ case Token::Type::kCompute:
+ return "compute";
+ case Token::Type::kConst:
+ return "const";
+ case Token::Type::kContinue:
+ return "continue";
+ case Token::Type::kContinuing:
+ return "continuing";
+ case Token::Type::kCoarse:
+ return "coarse";
+ case Token::Type::kDefault:
+ return "default";
+ case Token::Type::kDot:
+ return "dot";
+ case Token::Type::kDpdx:
+ return "dpdx";
+ case Token::Type::kDpdy:
+ return "dpdy";
+ case Token::Type::kElse:
+ return "else";
+ case Token::Type::kElseIf:
+ return "elseif";
+ case Token::Type::kEntryPoint:
+ return "entry_point";
+ case Token::Type::kF32:
+ return "f32";
+ case Token::Type::kFallthrough:
+ return "fallthrough";
+ case Token::Type::kFalse:
+ return "false";
+ case Token::Type::kFine:
+ return "fine";
+ case Token::Type::kFn:
+ return "fn";
+ case Token::Type::kFragCoord:
+ return "frag_coord";
+ case Token::Type::kFragDepth:
+ return "frag_depth";
+ case Token::Type::kFragment:
+ return "fragment";
+ case Token::Type::kFrontFacing:
+ return "front_facing";
+ case Token::Type::kFunction:
+ return "function";
+ case Token::Type::kFwidth:
+ return "fwidth";
+ case Token::Type::kGlobalInvocationId:
+ return "global_invocation_id";
+ case Token::Type::kI32:
+ return "i32";
+ case Token::Type::kIf:
+ return "if";
+ case Token::Type::kImage:
+ return "image";
+ case Token::Type::kImport:
+ return "import";
+ case Token::Type::kIn:
+ return "in";
+ case Token::Type::kInstanceIdx:
+ return "instance_idx";
+ case Token::Type::kIsNan:
+ return "is_nan";
+ case Token::Type::kIsInf:
+ return "is_inf";
+ case Token::Type::kIsFinite:
+ return "is_finite";
+ case Token::Type::kIsNormal:
+ return "is_normal";
+ case Token::Type::kKill:
+ return "kill";
+ case Token::Type::kLocalInvocationId:
+ return "local_invocation_id";
+ case Token::Type::kLocalInvocationIdx:
+ return "local_invocation_idx";
+ case Token::Type::kLocation:
+ return "location";
+ case Token::Type::kLoop:
+ return "loop";
+ case Token::Type::kMat2x2:
+ return "mat2x2";
+ case Token::Type::kMat2x3:
+ return "mat2x3";
+ case Token::Type::kMat2x4:
+ return "mat2x4";
+ case Token::Type::kMat3x2:
+ return "mat3x2";
+ case Token::Type::kMat3x3:
+ return "mat3x3";
+ case Token::Type::kMat3x4:
+ return "mat3x4";
+ case Token::Type::kMat4x2:
+ return "mat4x2";
+ case Token::Type::kMat4x3:
+ return "mat4x3";
+ case Token::Type::kMat4x4:
+ return "mat4x4";
+ case Token::Type::kNop:
+ return "nop";
+ case Token::Type::kNumWorkgroups:
+ return "num_workgroups";
+ case Token::Type::kOffset:
+ return "offset";
+ case Token::Type::kOut:
+ return "out";
+ case Token::Type::kOuterProduct:
+ return "outer_product";
+ case Token::Type::kPosition:
+ return "position";
+ case Token::Type::kPremerge:
+ return "premerge";
+ case Token::Type::kPrivate:
+ return "private";
+ case Token::Type::kPtr:
+ return "ptr";
+ case Token::Type::kPushConstant:
+ return "push_constant";
+ case Token::Type::kRegardless:
+ return "regardless";
+ case Token::Type::kReturn:
+ return "return";
+ case Token::Type::kSet:
+ return "set";
+ case Token::Type::kStorageBuffer:
+ return "storage_buffer";
+ case Token::Type::kStruct:
+ return "struct";
+ case Token::Type::kSwitch:
+ return "switch";
+ case Token::Type::kTrue:
+ return "true";
+ case Token::Type::kType:
+ return "type";
+ case Token::Type::kU32:
+ return "u32";
+ case Token::Type::kUniform:
+ return "uniform";
+ case Token::Type::kUniformConstant:
+ return "uniform_constant";
+ case Token::Type::kUnless:
+ return "unless";
+ case Token::Type::kVar:
+ return "var";
+ case Token::Type::kVec2:
+ return "vec2";
+ case Token::Type::kVec3:
+ return "vec3";
+ case Token::Type::kVec4:
+ return "vec4";
+ case Token::Type::kVertex:
+ return "vertex";
+ case Token::Type::kVertexIdx:
+ return "vertex_idx";
+ case Token::Type::kVoid:
+ return "void";
+ case Token::Type::kWorkgroup:
+ return "workgroup";
+ case Token::Type::kWorkgroupSize:
+ return "workgroup_size";
+ }
+
+ return "<unknown>";
+}
+
+Token::Token() : type_(Type::kUninitialized) {}
+
+Token::Token(Type type, const Source& source, const std::string& val)
+ : type_(type), source_(source), val_str_(val) {}
+
+Token::Token(const Source& source, uint32_t val)
+ : type_(Type::kUintLiteral), source_(source), val_uint_(val) {}
+
+Token::Token(const Source& source, int32_t val)
+ : type_(Type::kIntLiteral), source_(source), val_int_(val) {}
+
+Token::Token(const Source& source, float val)
+ : type_(Type::kFloatLiteral), source_(source), val_float_(val) {}
+
+Token::Token(Type type, const Source& source) : Token(type, source, "") {}
+
+Token::Token(Token&&) = default;
+
+Token::Token(const Token&) = default;
+
+Token::~Token() = default;
+
+Token& Token::operator=(const Token&) = default;
+
+std::string Token::to_str() const {
+ if (type_ == Type::kFloatLiteral) {
+ return std::to_string(val_float_);
+ }
+ if (type_ == Type::kIntLiteral) {
+ return std::to_string(val_int_);
+ }
+ if (type_ == Type::kUintLiteral) {
+ return std::to_string(val_uint_);
+ }
+ return val_str_;
+}
+
+float Token::to_f32() const {
+ return val_float_;
+}
+
+uint32_t Token::to_u32() const {
+ return val_uint_;
+}
+
+int32_t Token::to_i32() const {
+ return val_int_;
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
new file mode 100644
index 0000000..ad77a80
--- /dev/null
+++ b/src/reader/wgsl/token.h
@@ -0,0 +1,667 @@
+// Copyright 2020 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_READER_WGSL_TOKEN_H_
+#define SRC_READER_WGSL_TOKEN_H_
+
+#include <stddef.h>
+
+#include <ostream>
+#include <string>
+
+#include "src/source.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+/// Stores tokens generated by the Lexer
+class Token {
+ public:
+ /// The type of the parsed token
+ enum class Type {
+ /// Error result
+ kError = -2,
+ /// Reserved keyword
+ kReservedKeyword = -1,
+ /// Uninitialized token
+ kUninitialized = 0,
+ /// End of input string reached
+ kEOF,
+
+ /// An identifier
+ kIdentifier,
+ /// A string value
+ kStringLiteral,
+ /// A float value
+ kFloatLiteral,
+ /// An int value
+ kIntLiteral,
+ /// A uint value
+ kUintLiteral,
+
+ /// A '&'
+ kAnd,
+ /// A '&&'
+ kAndAnd,
+ /// A '->'
+ kArrow,
+ /// A '[['
+ kAttrLeft,
+ /// A ']]'
+ kAttrRight,
+ /// A '/'
+ kForwardSlash,
+ /// A '!'
+ kBang,
+ /// A '['
+ kBraceLeft,
+ /// A ']'
+ kBraceRight,
+ /// A '{'
+ kBracketLeft,
+ /// A '}'
+ kBracketRight,
+ /// A ':'
+ kColon,
+ /// A ','
+ kComma,
+ /// A '='
+ kEqual,
+ /// A '=='
+ kEqualEqual,
+ /// A '>'
+ kGreaterThan,
+ /// A '>='
+ kGreaterThanEqual,
+ /// A '<'
+ kLessThan,
+ /// A '<='
+ kLessThanEqual,
+ /// A '%'
+ kMod,
+ /// A '-'
+ kMinus,
+ /// A '::'
+ kNamespace,
+ /// A '!='
+ kNotEqual,
+ /// A '.'
+ kPeriod,
+ /// A '+'
+ kPlus,
+ /// A '|'
+ kOr,
+ /// A '||'
+ kOrOr,
+ /// A '('
+ kParenLeft,
+ /// A ')'
+ kParenRight,
+ /// A ';'
+ kSemicolon,
+ /// A '*'
+ kStar,
+ /// A '^'
+ kXor,
+
+ /// A 'all'
+ kAll,
+ /// A 'any'
+ kAny,
+ /// A 'array'
+ kArray,
+ /// A 'as'
+ kAs,
+ /// A 'binding'
+ kBinding,
+ /// A 'bool'
+ kBool,
+ /// A 'block'
+ kBlock,
+ /// A 'break'
+ kBreak,
+ /// A 'builtin'
+ kBuiltin,
+ /// A 'case'
+ kCase,
+ /// A 'cast'
+ kCast,
+ /// A 'compute'
+ kCompute,
+ /// A 'const'
+ kConst,
+ /// A 'continue'
+ kContinue,
+ /// A 'continuing'
+ kContinuing,
+ /// A 'coarse'
+ kCoarse,
+ /// A 'default'
+ kDefault,
+ /// A 'dot'
+ kDot,
+ /// A 'dpdx'
+ kDpdx,
+ /// A 'dpdy'
+ kDpdy,
+ /// A 'else'
+ kElse,
+ /// A 'elseif'
+ kElseIf,
+ /// A 'entry_point'
+ kEntryPoint,
+ /// A 'f32'
+ kF32,
+ /// A 'fallthrough'
+ kFallthrough,
+ /// A 'false'
+ kFalse,
+ /// A 'fine'
+ kFine,
+ /// A 'fn'
+ kFn,
+ /// A 'frag_coord'
+ kFragCoord,
+ // A 'frag_depth'
+ kFragDepth,
+ /// A 'fragment'
+ kFragment,
+ /// A 'front_facing'
+ kFrontFacing,
+ /// A 'function'
+ kFunction,
+ /// A 'fwidth'
+ kFwidth,
+ /// A 'global_invocation_id'
+ kGlobalInvocationId,
+ /// A 'i32'
+ kI32,
+ /// A 'if'
+ kIf,
+ /// A 'image'
+ kImage,
+ /// A 'import'
+ kImport,
+ /// A 'in'
+ kIn,
+ /// A 'instance_idx'
+ kInstanceIdx,
+ /// A 'is_nan'
+ kIsNan,
+ /// A 'is_inf'
+ kIsInf,
+ /// A 'is_finite'
+ kIsFinite,
+ /// A 'is_normal'
+ kIsNormal,
+ /// A 'kill'
+ kKill,
+ /// A 'local_invocation_id'
+ kLocalInvocationId,
+ /// A 'local_invocation_idx'
+ kLocalInvocationIdx,
+ /// A 'location'
+ kLocation,
+ /// A 'loop'
+ kLoop,
+ /// A 'mat2x2'
+ kMat2x2,
+ /// A 'mat2x3'
+ kMat2x3,
+ /// A 'mat2x4'
+ kMat2x4,
+ /// A 'mat3x2'
+ kMat3x2,
+ /// A 'mat3x3'
+ kMat3x3,
+ /// A 'mat3x4'
+ kMat3x4,
+ /// A 'mat4x2'
+ kMat4x2,
+ /// A 'mat4x3'
+ kMat4x3,
+ /// A 'mat4x4'
+ kMat4x4,
+ /// A 'nop'
+ kNop,
+ /// A 'num_workgroups'
+ kNumWorkgroups,
+ /// A 'offset'
+ kOffset,
+ /// A 'out'
+ kOut,
+ /// A 'outer_product'
+ kOuterProduct,
+ /// A 'position'
+ kPosition,
+ /// A 'premerge'
+ kPremerge,
+ /// A 'private'
+ kPrivate,
+ /// A 'ptr'
+ kPtr,
+ /// A 'push_constant'
+ kPushConstant,
+ /// A 'regardless'
+ kRegardless,
+ /// A 'return'
+ kReturn,
+ /// A 'set'
+ kSet,
+ /// A 'storage_buffer'
+ kStorageBuffer,
+ /// A 'struct'
+ kStruct,
+ /// A 'switch'
+ kSwitch,
+ /// A 'true'
+ kTrue,
+ /// A 'type'
+ kType,
+ /// A 'u32'
+ kU32,
+ /// A 'uniform'
+ kUniform,
+ /// A 'uniform_constant'
+ kUniformConstant,
+ /// A 'unless'
+ kUnless,
+ /// A 'var'
+ kVar,
+ /// A 'vec2'
+ kVec2,
+ /// A 'vec3'
+ kVec3,
+ /// A 'vec4'
+ kVec4,
+ /// A 'vertex'
+ kVertex,
+ /// A 'vertex_idx'
+ kVertexIdx,
+ /// A 'void'
+ kVoid,
+ /// A 'workgroup'
+ kWorkgroup,
+ /// A 'workgroup_size'
+ kWorkgroupSize
+ };
+
+ /// Converts a token type to a name
+ /// @param type the type to convert
+ /// @returns the token type as as string
+ static std::string TypeToName(Type type);
+
+ /// Creates an uninitialized token
+ Token();
+ /// Create a Token
+ /// @param type the Token::Type of the token
+ /// @param source the source of the token
+ Token(Type type, const Source& source);
+
+ /// Create a string Token
+ /// @param type the Token::Type of the token
+ /// @param source the source of the token
+ /// @param val the source string for the token
+ Token(Type type, const Source& source, const std::string& val);
+ /// Create a unsigned integer Token
+ /// @param source the source of the token
+ /// @param val the source unsigned for the token
+ Token(const Source& source, uint32_t val);
+ /// Create a signed integer Token
+ /// @param source the source of the token
+ /// @param val the source integer for the token
+ Token(const Source& source, int32_t val);
+ /// Create a float Token
+ /// @param source the source of the token
+ /// @param val the source float for the token
+ Token(const Source& source, float val);
+ /// Move constructor
+ Token(Token&&);
+ /// Copy constructor
+ Token(const Token&);
+ ~Token();
+
+ /// Assignment operator
+ /// @param b the token to copy
+ /// @return Token
+ Token& operator=(const Token& b);
+
+ /// Returns true if the token is of the given type
+ /// @param t the type to check against.
+ /// @returns true if the token is of type |t|
+ bool Is(Type t) const { return type_ == t; }
+
+ /// @returns true if the token is uninitialized
+ bool IsUninitialized() const { return type_ == Type::kUninitialized; }
+ /// @returns true if the token is reserved
+ bool IsReservedKeyword() const { return type_ == Type::kReservedKeyword; }
+ /// @returns true if the token is an error
+ bool IsError() const { return type_ == Type::kError; }
+ /// @returns true if the token is EOF
+ bool IsEof() const { return type_ == Type::kEOF; }
+ /// @returns true if the token is an identifier
+ bool IsIdentifier() const { return type_ == Type::kIdentifier; }
+ /// @returns true if the token is a string
+ bool IsStringLiteral() const { return type_ == Type::kStringLiteral; }
+ /// @returns true if the token is a float
+ bool IsFloatLiteral() const { return type_ == Type::kFloatLiteral; }
+ /// @returns true if the token is an int
+ bool IsIntLiteral() const { return type_ == Type::kIntLiteral; }
+ /// @returns true if the token is a unsigned int
+ bool IsUintLiteral() const { return type_ == Type::kUintLiteral; }
+
+ /// @returns true if token is a '&'
+ bool IsAnd() const { return type_ == Type::kAnd; }
+ /// @returns true if token is a '&&'
+ bool IsAndAnd() const { return type_ == Type::kAndAnd; }
+ /// @returns true if token is a '->'
+ bool IsArrow() const { return type_ == Type::kArrow; }
+ /// @returns true if token is a '[['
+ bool IsAttrLeft() const { return type_ == Type::kAttrLeft; }
+ /// @returns true if token is a ']]'
+ bool IsAttrRight() const { return type_ == Type::kAttrRight; }
+ /// @returns true if token is a '/'
+ bool IsForwardSlash() const { return type_ == Type::kForwardSlash; }
+ /// @returns true if token is a '!'
+ bool IsBang() const { return type_ == Type::kBang; }
+ /// @returns true if token is a '['
+ bool IsBraceLeft() const { return type_ == Type::kBraceLeft; }
+ /// @returns true if token is a ']'
+ bool IsBraceRight() const { return type_ == Type::kBraceRight; }
+ /// @returns true if token is a '{'
+ bool IsBracketLeft() const { return type_ == Type::kBracketLeft; }
+ /// @returns true if token is a '}'
+ bool IsBracketRight() const { return type_ == Type::kBracketRight; }
+ /// @returns true if token is a ':'
+ bool IsColon() const { return type_ == Type::kColon; }
+ /// @returns true if token is a ','
+ bool IsComma() const { return type_ == Type::kComma; }
+ /// @returns true if token is a '='
+ bool IsEqual() const { return type_ == Type::kEqual; }
+ /// @returns true if token is a '=='
+ bool IsEqualEqual() const { return type_ == Type::kEqualEqual; }
+ /// @returns true if token is a '>'
+ bool IsGreaterThan() const { return type_ == Type::kGreaterThan; }
+ /// @returns true if token is a '>='
+ bool IsGreaterThanEqual() const { return type_ == Type::kGreaterThanEqual; }
+ /// @returns true if token is a '<'
+ bool IsLessThan() const { return type_ == Type::kLessThan; }
+ /// @returns true if token is a '<='
+ bool IsLessThanEqual() const { return type_ == Type::kLessThanEqual; }
+ /// @returns true if token is a '%'
+ bool IsMod() const { return type_ == Type::kMod; }
+ /// @returns true if token is a '-'
+ bool IsMinus() const { return type_ == Type::kMinus; }
+ /// @returns true if token is a '::'
+ bool IsNamespace() const { return type_ == Type::kNamespace; }
+ /// @returns true if token is a '!='
+ bool IsNotEqual() const { return type_ == Type::kNotEqual; }
+ /// @returns true if token is a '.'
+ bool IsPeriod() const { return type_ == Type::kPeriod; }
+ /// @returns true if token is a '+'
+ bool IsPlus() const { return type_ == Type::kPlus; }
+ /// @returns true if token is a '|'
+ bool IsOr() const { return type_ == Type::kOr; }
+ /// @returns true if token is a '||'
+ bool IsOrOr() const { return type_ == Type::kOrOr; }
+ /// @returns true if token is a '('
+ bool IsParenLeft() const { return type_ == Type::kParenLeft; }
+ /// @returns true if token is a ')'
+ bool IsParenRight() const { return type_ == Type::kParenRight; }
+ /// @returns true if token is a ';'
+ bool IsSemicolon() const { return type_ == Type::kSemicolon; }
+ /// @returns true if token is a '*'
+ bool IsStar() const { return type_ == Type::kStar; }
+ /// @returns true if token is a '^'
+ bool IsXor() const { return type_ == Type::kXor; }
+
+ /// @returns true if token is a 'all'
+ bool IsAll() const { return type_ == Type::kAll; }
+ /// @returns true if token is a 'any'
+ bool IsAny() const { return type_ == Type::kAny; }
+ /// @returns true if token is a 'array'
+ bool IsArray() const { return type_ == Type::kArray; }
+ /// @returns true if token is a 'as'
+ bool IsAs() const { return type_ == Type::kAs; }
+ /// @returns true if token is a 'binding'
+ bool IsBinding() const { return type_ == Type::kBinding; }
+ /// @returns true if token is a 'block'
+ bool IsBlock() const { return type_ == Type::kBlock; }
+ /// @returns true if token is a 'bool'
+ bool IsBool() const { return type_ == Type::kBool; }
+ /// @returns true if token is a 'break'
+ bool IsBreak() const { return type_ == Type::kBreak; }
+ /// @returns true if token is a 'builtin'
+ bool IsBuiltin() const { return type_ == Type::kBuiltin; }
+ /// @returns true if token is a 'case'
+ bool IsCase() const { return type_ == Type::kCase; }
+ /// @returns true if token is a 'cast'
+ bool IsCast() const { return type_ == Type::kCast; }
+ /// @returns true if token is 'coarse'
+ bool IsCoarse() const { return type_ == Type::kCoarse; }
+ /// @returns true if token is a 'compute'
+ bool IsCompute() const { return type_ == Type::kCompute; }
+ /// @returns true if token is a 'const'
+ bool IsConst() const { return type_ == Type::kConst; }
+ /// @returns true if token is a 'continue'
+ bool IsContinue() const { return type_ == Type::kContinue; }
+ /// @returns true if token is a 'continuing'
+ bool IsContinuing() const { return type_ == Type::kContinuing; }
+ /// @returns true if token is a 'default'
+ bool IsDefault() const { return type_ == Type::kDefault; }
+ /// @returns true if token is a 'dot'
+ bool IsDot() const { return type_ == Type::kDot; }
+ /// @returns true if token is a 'dpdx'
+ bool IsDpdx() const { return type_ == Type::kDpdx; }
+ /// @returns true if token is a 'dpdy'
+ bool IsDpdy() const { return type_ == Type::kDpdy; }
+ /// @returns true if token is a 'else'
+ bool IsElse() const { return type_ == Type::kElse; }
+ /// @returns true if token is a 'elseif'
+ bool IsElseIf() const { return type_ == Type::kElseIf; }
+ /// @returns true if token is a 'entry_point'
+ bool IsEntryPoint() const { return type_ == Type::kEntryPoint; }
+ /// @returns true if token is a 'f32'
+ bool IsF32() const { return type_ == Type::kF32; }
+ /// @returns true if token is a 'fallthrough'
+ bool IsFallthrough() const { return type_ == Type::kFallthrough; }
+ /// @returns true if token is a 'false'
+ bool IsFalse() const { return type_ == Type::kFalse; }
+ /// @returns true if token is a 'fine'
+ bool IsFine() const { return type_ == Type::kFine; }
+ /// @returns true if token is a 'fn'
+ bool IsFn() const { return type_ == Type::kFn; }
+ /// @returns true if token is a 'frag_coord'
+ bool IsFragCoord() const { return type_ == Type::kFragCoord; }
+ /// @returns true if token is a 'frag_depth'
+ bool IsFragDepth() const { return type_ == Type::kFragDepth; }
+ /// @returns true if token is a 'fragment'
+ bool IsFragment() const { return type_ == Type::kFragment; }
+ /// @returns true if token is a 'front_facing'
+ bool IsFrontFacing() const { return type_ == Type::kFrontFacing; }
+ /// @returns true if token is a 'function'
+ bool IsFunction() const { return type_ == Type::kFunction; }
+ /// @returns true if token is a 'fwidth'
+ bool IsFwidth() const { return type_ == Type::kFwidth; }
+ /// @returns true if token is a 'global_invocation_id'
+ bool IsGlobalInvocationId() const {
+ return type_ == Type::kGlobalInvocationId;
+ }
+ /// @returns true if token is a 'i32'
+ bool IsI32() const { return type_ == Type::kI32; }
+ /// @returns true if token is a 'if'
+ bool IsIf() const { return type_ == Type::kIf; }
+ /// @returns true if token is a 'image'
+ bool IsImage() const { return type_ == Type::kImage; }
+ /// @returns true if token is a 'import'
+ bool IsImport() const { return type_ == Type::kImport; }
+ /// @returns true if token is a 'in'
+ bool IsIn() const { return type_ == Type::kIn; }
+ /// @returns true if token is a 'instance_idx'
+ bool IsInstanceIdx() const { return type_ == Type::kInstanceIdx; }
+ /// @returns true if token is a 'is_nan'
+ bool IsIsNan() const { return type_ == Type::kIsNan; }
+ /// @returns true if token is a 'is_inf'
+ bool IsIsInf() const { return type_ == Type::kIsInf; }
+ /// @returns true if token is a 'is_finite'
+ bool IsIsFinite() const { return type_ == Type::kIsFinite; }
+ /// @returns true if token is a 'is_normal'
+ bool IsIsNormal() const { return type_ == Type::kIsNormal; }
+ /// @returns true if token is a 'kill'
+ bool IsKill() const { return type_ == Type::kKill; }
+ /// @returns true if token is a 'local_invocation_id'
+ bool IsLocalInvocationId() const { return type_ == Type::kLocalInvocationId; }
+ /// @returns true if token is a 'local_invocation_idx'
+ bool IsLocalInvocationIdx() const {
+ return type_ == Type::kLocalInvocationIdx;
+ }
+ /// @returns true if token is a 'location'
+ bool IsLocation() const { return type_ == Type::kLocation; }
+ /// @returns true if token is a 'loop'
+ bool IsLoop() const { return type_ == Type::kLoop; }
+ /// @returns true if token is a 'mat2x2'
+ bool IsMat2x2() const { return type_ == Type::kMat2x2; }
+ /// @returns true if token is a 'mat2x3'
+ bool IsMat2x3() const { return type_ == Type::kMat2x3; }
+ /// @returns true if token is a 'mat2x4'
+ bool IsMat2x4() const { return type_ == Type::kMat2x4; }
+ /// @returns true if token is a 'mat3x2'
+ bool IsMat3x2() const { return type_ == Type::kMat3x2; }
+ /// @returns true if token is a 'mat3x3'
+ bool IsMat3x3() const { return type_ == Type::kMat3x3; }
+ /// @returns true if token is a 'mat3x4'
+ bool IsMat3x4() const { return type_ == Type::kMat3x4; }
+ /// @returns true if token is a 'mat4x2'
+ bool IsMat4x2() const { return type_ == Type::kMat4x2; }
+ /// @returns true if token is a 'mat4x3'
+ bool IsMat4x3() const { return type_ == Type::kMat4x3; }
+ /// @returns true if token is a 'mat4x4'
+ bool IsMat4x4() const { return type_ == Type::kMat4x4; }
+ /// @returns true if token is a 'nop'
+ bool IsNop() const { return type_ == Type::kNop; }
+ /// @returns true if token is a 'num_workgroups'
+ bool IsNumWorkgroups() const { return type_ == Type::kNumWorkgroups; }
+ /// @returns true if token is a 'offset'
+ bool IsOffset() const { return type_ == Type::kOffset; }
+ /// @returns true if token is a 'out'
+ bool IsOut() const { return type_ == Type::kOut; }
+ /// @returns true if token is a 'outer_product'
+ bool IsOuterProduct() const { return type_ == Type::kOuterProduct; }
+ /// @returns true if token is a 'position'
+ bool IsPosition() const { return type_ == Type::kPosition; }
+ /// @returns true if token is a 'premerge'
+ bool IsPremerge() const { return type_ == Type::kPremerge; }
+ /// @returns true if token is a 'private'
+ bool IsPrivate() const { return type_ == Type::kPrivate; }
+ /// @returns true if token is a 'ptr'
+ bool IsPtr() const { return type_ == Type::kPtr; }
+ /// @returns true if token is a 'push_constant'
+ bool IsPushConstant() const { return type_ == Type::kPushConstant; }
+ /// @returns true if token is a 'regardless'
+ bool IsRegardless() const { return type_ == Type::kRegardless; }
+ /// @returns true if token is a 'return'
+ bool IsReturn() const { return type_ == Type::kReturn; }
+ /// @returns true if token is a 'set'
+ bool IsSet() const { return type_ == Type::kSet; }
+ /// @returns true if token is a 'storage_buffer'
+ bool IsStorageBuffer() const { return type_ == Type::kStorageBuffer; }
+ /// @returns true if token is a 'struct'
+ bool IsStruct() const { return type_ == Type::kStruct; }
+ /// @returns true if token is a 'switch'
+ bool IsSwitch() const { return type_ == Type::kSwitch; }
+ /// @returns true if token is a 'true'
+ bool IsTrue() const { return type_ == Type::kTrue; }
+ /// @returns true if token is a 'type'
+ bool IsType() const { return type_ == Type::kType; }
+ /// @returns true if token is a 'u32'
+ bool IsU32() const { return type_ == Type::kU32; }
+ /// @returns true if token is a 'uniform'
+ bool IsUniform() const { return type_ == Type::kUniform; }
+ /// @returns true if token is a 'uniform_constant'
+ bool IsUniformConstant() const { return type_ == Type::kUniformConstant; }
+ /// @returns true if token is a 'unless'
+ bool IsUnless() const { return type_ == Type::kUnless; }
+ /// @returns true if token is a 'var'
+ bool IsVar() const { return type_ == Type::kVar; }
+ /// @returns true if token is a 'vec2'
+ bool IsVec2() const { return type_ == Type::kVec2; }
+ /// @returns true if token is a 'vec3'
+ bool IsVec3() const { return type_ == Type::kVec3; }
+ /// @returns true if token is a 'vec4'
+ bool IsVec4() const { return type_ == Type::kVec4; }
+ /// @returns true if token is a 'vertex'
+ bool IsVertex() const { return type_ == Type::kVertex; }
+ /// @returns true if token is a 'vertex_idx'
+ bool IsVertexIdx() const { return type_ == Type::kVertexIdx; }
+ /// @returns true if token is a 'void'
+ bool IsVoid() const { return type_ == Type::kVoid; }
+ /// @returns true if token is a 'workgroup'
+ bool IsWorkgroup() const { return type_ == Type::kWorkgroup; }
+ /// @returns true if token is a 'workgroup_size'
+ bool IsWorkgroupSize() const { return type_ == Type::kWorkgroupSize; }
+
+ /// @returns the source line of the token
+ size_t line() const { return source_.line; }
+ /// @returns the source column of the token
+ size_t column() const { return source_.column; }
+ /// @returns the source information for this token
+ Source source() const { return source_; }
+
+ /// Returns the string value of the token
+ /// @return const std::string&
+ std::string to_str() const;
+ /// Returns the float value of the token. 0 is returned if the token does not
+ /// contain a float value.
+ /// @return float
+ float to_f32() const;
+ /// Returns the uint32 value of the token. 0 is returned if the token does not
+ /// contain a unsigned integer value.
+ /// @return uint32_t
+ uint32_t to_u32() const;
+ /// Returns the int32 value of the token. 0 is returned if the token does not
+ /// contain a signed integer value.
+ /// @return int32_t
+ int32_t to_i32() const;
+
+ /// @returns the token type as string
+ std::string to_name() const { return Token::TypeToName(type_); }
+
+ private:
+ /// The Token::Type of the token
+ Type type_ = Type::kError;
+ /// The source where the token appeared
+ Source source_;
+ /// The string represented by the token
+ std::string val_str_;
+ /// The signed integer represented by the token
+ int32_t val_int_ = 0;
+ /// The unsigned integer represented by the token
+ uint32_t val_uint_ = 0;
+ /// The float value represented by the token
+ float val_float_ = 0.0;
+};
+
+#ifndef NDEBUG
+inline std::ostream& operator<<(std::ostream& out, Token::Type type) {
+ out << Token::TypeToName(type);
+ return out;
+}
+#endif // NDEBUG
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
+
+#endif // SRC_READER_WGSL_TOKEN_H_
diff --git a/src/reader/wgsl/token_test.cc b/src/reader/wgsl/token_test.cc
new file mode 100644
index 0000000..d4fbc42
--- /dev/null
+++ b/src/reader/wgsl/token_test.cc
@@ -0,0 +1,76 @@
+// Copyright 2020 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/reader/wgsl/token.h"
+
+#include <limits>
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace reader {
+namespace wgsl {
+
+using TokenTest = testing::Test;
+
+TEST_F(TokenTest, ReturnsStr) {
+ Token t(Token::Type::kStringLiteral, Source{1, 1}, "test string");
+ EXPECT_EQ(t.to_str(), "test string");
+}
+
+TEST_F(TokenTest, ReturnsF32) {
+ Token t1(Source{1, 1}, -2.345f);
+ EXPECT_EQ(t1.to_f32(), -2.345f);
+
+ Token t2(Source{1, 1}, 2.345f);
+ EXPECT_EQ(t2.to_f32(), 2.345f);
+}
+
+TEST_F(TokenTest, ReturnsI32) {
+ Token t1(Source{1, 1}, -2345);
+ EXPECT_EQ(t1.to_i32(), -2345);
+
+ Token t2(Source{1, 1}, 2345);
+ EXPECT_EQ(t2.to_i32(), 2345);
+}
+
+TEST_F(TokenTest, HandlesMaxI32) {
+ Token t1(Source{1, 1}, std::numeric_limits<int32_t>::max());
+ EXPECT_EQ(t1.to_i32(), std::numeric_limits<int32_t>::max());
+}
+
+TEST_F(TokenTest, HandlesMinI32) {
+ Token t1(Source{1, 1}, std::numeric_limits<int32_t>::min());
+ EXPECT_EQ(t1.to_i32(), std::numeric_limits<int32_t>::min());
+}
+
+TEST_F(TokenTest, ReturnsU32) {
+ Token t2(Source{1, 1}, 2345u);
+ EXPECT_EQ(t2.to_u32(), 2345);
+}
+
+TEST_F(TokenTest, ReturnsMaxU32) {
+ Token t1(Source{1, 1}, std::numeric_limits<uint32_t>::max());
+ EXPECT_EQ(t1.to_u32(), std::numeric_limits<uint32_t>::max());
+}
+
+TEST_F(TokenTest, Source) {
+ Token t(Token::Type::kUintLiteral, Source{3, 9});
+ EXPECT_EQ(t.line(), 3);
+ EXPECT_EQ(t.column(), 9);
+}
+
+} // namespace wgsl
+} // namespace reader
+} // namespace tint
diff --git a/src/source.h b/src/source.h
new file mode 100644
index 0000000..0305350
--- /dev/null
+++ b/src/source.h
@@ -0,0 +1,33 @@
+
+// Copyright 2020 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_SOURCE_H_
+#define SRC_SOURCE_H_
+
+#include <stddef.h>
+
+namespace tint {
+
+/// Represents a line and column position
+struct Source {
+ /// The line the token appeared on
+ size_t line = 0;
+ /// The column the token appeared in
+ size_t column = 0;
+};
+
+} // namespace tint
+
+#endif // SRC_SOURCE_H_
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
new file mode 100644
index 0000000..0faf0af
--- /dev/null
+++ b/src/type_determiner.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/type_determiner.h"
+
+namespace tint {
+
+TypeDeterminer::TypeDeterminer() = default;
+
+TypeDeterminer::~TypeDeterminer() = default;
+
+bool TypeDeterminer::Determine(ast::Module*) {
+ return true;
+}
+
+} // namespace tint
diff --git a/src/type_determiner.h b/src/type_determiner.h
new file mode 100644
index 0000000..8f2062e
--- /dev/null
+++ b/src/type_determiner.h
@@ -0,0 +1,45 @@
+// Copyright 2020 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_TYPE_DETERMINER_H_
+#define SRC_TYPE_DETERMINER_H_
+
+#include <string>
+
+#include "src/ast/module.h"
+
+namespace tint {
+
+/// Determines types for all items in the given tint module
+class TypeDeterminer {
+ public:
+ /// Constructor
+ TypeDeterminer();
+ ~TypeDeterminer();
+
+ /// Runs the type determiner
+ /// @param module the module to update with typing information
+ /// @returns true if the type determiner was successful
+ bool Determine(ast::Module* module);
+
+ /// @returns error messages from the type determiner
+ const std::string& error() { return error_; }
+
+ private:
+ std::string error_;
+};
+
+} // namespace tint
+
+#endif // SRC_TYPE_DETERMINER_H_
diff --git a/src/type_manager.cc b/src/type_manager.cc
new file mode 100644
index 0000000..5380f5f
--- /dev/null
+++ b/src/type_manager.cc
@@ -0,0 +1,53 @@
+// Copyright 2020 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/type_manager.h"
+
+#include <utility>
+
+namespace tint {
+namespace {
+
+TypeManager* manager_ = nullptr;
+
+} // namespace
+
+// static
+TypeManager* TypeManager::Instance() {
+ if (!manager_) {
+ manager_ = new TypeManager();
+ }
+ return manager_;
+}
+
+// static
+void TypeManager::Destroy() {
+ delete manager_;
+ manager_ = nullptr;
+}
+
+TypeManager::TypeManager() = default;
+
+TypeManager::~TypeManager() = default;
+
+ast::type::Type* TypeManager::Get(std::unique_ptr<ast::type::Type> type) {
+ auto name = type->type_name();
+
+ if (types_.find(name) == types_.end()) {
+ types_[name] = std::move(type);
+ }
+ return types_.find(name)->second.get();
+}
+
+} // namespace tint
diff --git a/src/type_manager.h b/src/type_manager.h
new file mode 100644
index 0000000..573542a
--- /dev/null
+++ b/src/type_manager.h
@@ -0,0 +1,49 @@
+// Copyright 2020 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_TYPE_MANAGER_H_
+#define SRC_TYPE_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "src/ast/type/type.h"
+
+namespace tint {
+
+/// The type manager holds all the pointers to the known types.
+class TypeManager {
+ public:
+ /// @returns a pointer to the type manager
+ static TypeManager* Instance();
+ /// Frees the type manager and any associated types. The types should not be
+ /// used after the manager is freed.
+ static void Destroy();
+
+ /// Get the given type from the type manager
+ /// @param type The type to register
+ /// @return the pointer to the registered type
+ ast::type::Type* Get(std::unique_ptr<ast::type::Type> type);
+
+ private:
+ TypeManager();
+ ~TypeManager();
+
+ std::unordered_map<std::string, std::unique_ptr<ast::type::Type>> types_;
+};
+
+} // namespace tint
+
+#endif // SRC_TYPE_MANAGER_H_
diff --git a/src/type_manager_test.cc b/src/type_manager_test.cc
new file mode 100644
index 0000000..821e559
--- /dev/null
+++ b/src/type_manager_test.cc
@@ -0,0 +1,81 @@
+// Copyright 2020 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/type_manager.h"
+
+#include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/u32_type.h"
+
+namespace tint {
+
+using TypeManagerTest = testing::Test;
+
+TEST_F(TypeManagerTest, Singleton) {
+ auto tm = TypeManager::Instance();
+ ASSERT_NE(tm, nullptr);
+ ASSERT_EQ(tm, TypeManager::Instance());
+
+ TypeManager::Destroy();
+}
+
+TEST_F(TypeManagerTest, Destroy) {
+ auto tm = TypeManager::Instance();
+ ASSERT_NE(tm, nullptr);
+ ASSERT_EQ(tm, TypeManager::Instance());
+
+ TypeManager::Destroy();
+
+ tm = TypeManager::Instance();
+ ASSERT_NE(tm, nullptr);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(TypeManagerTest, GetUnregistered) {
+ auto tm = TypeManager::Instance();
+ auto t = tm->Get(std::make_unique<ast::type::I32Type>());
+ ASSERT_NE(t, nullptr);
+ EXPECT_TRUE(t->IsI32());
+
+ TypeManager::Destroy();
+}
+
+TEST_F(TypeManagerTest, GetSameTypeReturnsSamePtr) {
+ auto tm = TypeManager::Instance();
+ auto t = tm->Get(std::make_unique<ast::type::I32Type>());
+ ASSERT_NE(t, nullptr);
+ EXPECT_TRUE(t->IsI32());
+
+ auto t2 = tm->Get(std::make_unique<ast::type::I32Type>());
+ EXPECT_EQ(t, t2);
+
+ TypeManager::Destroy();
+}
+
+TEST_F(TypeManagerTest, GetDifferentTypeReturnsDifferentPtr) {
+ auto tm = TypeManager::Instance();
+ auto t = tm->Get(std::make_unique<ast::type::I32Type>());
+ ASSERT_NE(t, nullptr);
+ EXPECT_TRUE(t->IsI32());
+
+ auto t2 = tm->Get(std::make_unique<ast::type::U32Type>());
+ ASSERT_NE(t2, nullptr);
+ EXPECT_NE(t, t2);
+ EXPECT_TRUE(t2->IsU32());
+
+ TypeManager::Destroy();
+}
+
+} // namespace tint
diff --git a/src/validator.cc b/src/validator.cc
new file mode 100644
index 0000000..1ee1c4c
--- /dev/null
+++ b/src/validator.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/validator.h"
+
+namespace tint {
+
+Validator::Validator() = default;
+
+Validator::~Validator() = default;
+
+bool Validator::Validate(const ast::Module&) {
+ return true;
+}
+
+} // namespace tint
diff --git a/src/validator.h b/src/validator.h
new file mode 100644
index 0000000..32c64b7
--- /dev/null
+++ b/src/validator.h
@@ -0,0 +1,45 @@
+// Copyright 2020 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_VALIDATOR_H_
+#define SRC_VALIDATOR_H_
+
+#include <string>
+
+#include "src/ast/module.h"
+
+namespace tint {
+
+/// Determines if the module is complete and valid
+class Validator {
+ public:
+ /// Constructor
+ Validator();
+ ~Validator();
+
+ /// Runs the validator
+ /// @param module the module to validate
+ /// @returns true if the validation was successful
+ bool Validate(const ast::Module& module);
+
+ /// @returns error messages from the validator
+ const std::string& error() { return error_; }
+
+ private:
+ std::string error_;
+};
+
+} // namespace tint
+
+#endif // SRC_VALIDATOR_H_
diff --git a/src/writer/spv/generator.cc b/src/writer/spv/generator.cc
new file mode 100644
index 0000000..8d15522
--- /dev/null
+++ b/src/writer/spv/generator.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 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/writer/spv/generator.h"
+
+#include <utility>
+
+namespace tint {
+namespace writer {
+namespace spv {
+
+Generator::Generator(ast::Module module) : writer::Writer(std::move(module)) {}
+
+Generator::~Generator() = default;
+
+bool Generator::Generate() {
+ return true;
+}
+
+} // namespace spv
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/spv/generator.h b/src/writer/spv/generator.h
new file mode 100644
index 0000000..e677019
--- /dev/null
+++ b/src/writer/spv/generator.h
@@ -0,0 +1,49 @@
+// Copyright 2020 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_WRITER_SPV_GENERATOR_H_
+#define SRC_WRITER_SPV_GENERATOR_H_
+
+#include <vector>
+
+#include "src/writer/writer.h"
+
+namespace tint {
+namespace writer {
+namespace spv {
+
+/// Class to generate SPIR-V from a Tint module
+class Generator : public writer::Writer {
+ public:
+ /// Constructor
+ /// @param module the module to convert
+ explicit Generator(ast::Module module);
+ ~Generator() override;
+
+ /// Generates the result data
+ /// @returns true on successful generation; false otherwise
+ bool Generate() override;
+
+ /// @returns the result data
+ const std::vector<uint32_t>& result() const { return result_; }
+
+ private:
+ std::vector<uint32_t> result_;
+};
+
+} // namespace spv
+} // namespace writer
+} // namespace tint
+
+#endif // SRC_WRITER_SPV_GENERATOR_H_
diff --git a/src/writer/wgsl/generator.cc b/src/writer/wgsl/generator.cc
new file mode 100644
index 0000000..015e99c
--- /dev/null
+++ b/src/writer/wgsl/generator.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 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/writer/wgsl/generator.h"
+
+#include <utility>
+
+namespace tint {
+namespace writer {
+namespace wgsl {
+
+Generator::Generator(ast::Module module) : writer::Writer(std::move(module)) {}
+
+Generator::~Generator() = default;
+
+bool Generator::Generate() {
+ return true;
+}
+
+} // namespace wgsl
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/wgsl/generator.h b/src/writer/wgsl/generator.h
new file mode 100644
index 0000000..b0a00e2
--- /dev/null
+++ b/src/writer/wgsl/generator.h
@@ -0,0 +1,49 @@
+// Copyright 2020 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_WRITER_WGSL_GENERATOR_H_
+#define SRC_WRITER_WGSL_GENERATOR_H_
+
+#include <string>
+
+#include "src/writer/writer.h"
+
+namespace tint {
+namespace writer {
+namespace wgsl {
+
+/// Class to generate WGSL source from a WGSL module
+class Generator : public writer::Writer {
+ public:
+ /// Constructor
+ /// @param module the module to convert
+ explicit Generator(ast::Module module);
+ ~Generator() override;
+
+ /// Generates the result data
+ /// @returns true on successful generation; false otherwise
+ bool Generate() override;
+
+ /// @returns the result data
+ const std::string& result() const { return result_; }
+
+ private:
+ std::string result_;
+};
+
+} // namespace wgsl
+} // namespace writer
+} // namespace tint
+
+#endif // SRC_WRITER_WGSL_GENERATOR_H_
diff --git a/src/writer/writer.cc b/src/writer/writer.cc
new file mode 100644
index 0000000..545ed3b
--- /dev/null
+++ b/src/writer/writer.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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/writer/writer.h"
+
+#include <utility>
+
+namespace tint {
+namespace writer {
+
+Writer::Writer(ast::Module module) : module_(std::move(module)) {}
+
+Writer::~Writer() = default;
+
+} // namespace writer
+} // namespace tint
diff --git a/src/writer/writer.h b/src/writer/writer.h
new file mode 100644
index 0000000..b4a7840
--- /dev/null
+++ b/src/writer/writer.h
@@ -0,0 +1,55 @@
+// Copyright 2020 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_WRITER_WRITER_H_
+#define SRC_WRITER_WRITER_H_
+
+#include <string>
+
+#include "src/ast/module.h"
+
+namespace tint {
+namespace writer {
+
+/// Base class for the output writers
+class Writer {
+ public:
+ virtual ~Writer();
+
+ /// @returns the writer error string
+ const std::string& error() const { return error_; }
+
+ /// Converts the module into the desired format
+ /// @returns true on success; false on failure
+ virtual bool Generate() = 0;
+
+ protected:
+ /// Constructor
+ /// @param module the tint module to convert
+ explicit Writer(ast::Module module);
+
+ /// Sets the error string
+ /// @param msg the error message
+ void set_error(const std::string& msg) { error_ = msg; }
+
+ /// An error message, if an error was encountered
+ std::string error_;
+ /// The module being converted
+ ast::Module module_;
+};
+
+} // namespace writer
+} // namespace tint
+
+#endif // SRC_WRITER_WRITER_H_