mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:10:12 +01:00
Putting fuzzer files under //testing to emphasize the testonly aspect (consolidates bazel and common subdirectories). The attributes on explorer_fuzzer are also a little skewed from what's desirable; it's been working okay, but this should still be a refinement.
1026 lines
30 KiB
C++
1026 lines
30 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "testing/fuzzing/proto_to_carbon.h"
|
|
|
|
#include <google/protobuf/text_format.h>
|
|
|
|
#include <string_view>
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "testing/fuzzing/carbon.pb.h"
|
|
|
|
namespace Carbon {
|
|
|
|
static auto ExpressionToCarbon(const Fuzzing::Expression& expression,
|
|
llvm::raw_ostream& out) -> void;
|
|
static auto PatternToCarbon(const Fuzzing::Pattern& pattern,
|
|
llvm::raw_ostream& out) -> void;
|
|
static auto StatementToCarbon(const Fuzzing::Statement& statement,
|
|
llvm::raw_ostream& out) -> void;
|
|
static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
|
|
llvm::raw_ostream& out) -> void;
|
|
|
|
// Produces a valid Carbon identifier, which must match the regex
|
|
// `[A-Za-z_][A-Za-z0-9_]*`. In the case when `s` is generated by the
|
|
// fuzzing framework, it might contain invalid/non-printable characters.
|
|
static auto IdentifierToCarbon(std::string_view s, llvm::raw_ostream& out)
|
|
-> void {
|
|
if (s.empty()) {
|
|
out << "EmptyIdentifier";
|
|
} else {
|
|
if (!llvm::isAlpha(s[0]) && s[0] != '_') {
|
|
// Ensures that identifier starts with a valid character.
|
|
out << 'x';
|
|
}
|
|
for (const char c : s) {
|
|
if (llvm::isAlnum(c) || c == '_') {
|
|
out << c;
|
|
} else {
|
|
out << llvm::toHex(c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static auto StringLiteralToCarbon(std::string_view s, llvm::raw_ostream& out) {
|
|
out << '"';
|
|
out.write_escaped(s, /*UseHexEscapes=*/true);
|
|
out << '"';
|
|
}
|
|
|
|
static auto LibraryNameToCarbon(const Fuzzing::LibraryName& library,
|
|
llvm::raw_ostream& out) -> void {
|
|
IdentifierToCarbon(library.package_name(), out);
|
|
|
|
// Path is optional.
|
|
if (library.has_path()) {
|
|
out << " library ";
|
|
// library.path() is a string literal.
|
|
StringLiteralToCarbon(library.path(), out);
|
|
}
|
|
}
|
|
|
|
static auto PrefixUnaryOperatorToCarbon(std::string_view op,
|
|
const Fuzzing::Expression& arg,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << op;
|
|
ExpressionToCarbon(arg, out);
|
|
}
|
|
|
|
static auto PostfixUnaryOperatorToCarbon(const Fuzzing::Expression& arg,
|
|
std::string_view op,
|
|
llvm::raw_ostream& out) -> void {
|
|
ExpressionToCarbon(arg, out);
|
|
out << op;
|
|
}
|
|
|
|
static auto BinaryOperatorToCarbon(const Fuzzing::Expression& lhs,
|
|
std::string_view op,
|
|
const Fuzzing::Expression& rhs,
|
|
llvm::raw_ostream& out) -> void {
|
|
ExpressionToCarbon(lhs, out);
|
|
out << op;
|
|
ExpressionToCarbon(rhs, out);
|
|
}
|
|
|
|
static auto OperatorToCarbon(const Fuzzing::OperatorExpression& operator_expr,
|
|
llvm::raw_ostream& out) -> void {
|
|
const Fuzzing::Expression& arg0 =
|
|
!operator_expr.arguments().empty()
|
|
? operator_expr.arguments(0)
|
|
: Fuzzing::Expression::default_instance();
|
|
const Fuzzing::Expression& arg1 =
|
|
operator_expr.arguments().size() > 1
|
|
? operator_expr.arguments(1)
|
|
: Fuzzing::Expression::default_instance();
|
|
out << "(";
|
|
switch (operator_expr.op()) {
|
|
case Fuzzing::OperatorExpression::UnknownOperator:
|
|
// `-` is an arbitrary default to avoid getting invalid syntax.
|
|
PrefixUnaryOperatorToCarbon("-", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::AddressOf:
|
|
PrefixUnaryOperatorToCarbon("&", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::As:
|
|
BinaryOperatorToCarbon(arg0, " as ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Deref:
|
|
PrefixUnaryOperatorToCarbon("*", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Mul:
|
|
BinaryOperatorToCarbon(arg0, " * ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Div:
|
|
BinaryOperatorToCarbon(arg0, " / ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Mod:
|
|
BinaryOperatorToCarbon(arg0, " % ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Ptr:
|
|
PostfixUnaryOperatorToCarbon(arg0, "*", out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Neg:
|
|
PrefixUnaryOperatorToCarbon("-", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Sub:
|
|
BinaryOperatorToCarbon(arg0, " - ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Not:
|
|
// Needs a space to 'unglue' from the operand.
|
|
PrefixUnaryOperatorToCarbon("not ", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Add:
|
|
BinaryOperatorToCarbon(arg0, " + ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::And:
|
|
BinaryOperatorToCarbon(arg0, " and ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Eq:
|
|
BinaryOperatorToCarbon(arg0, " == ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::Less:
|
|
BinaryOperatorToCarbon(arg0, " < ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::LessEq:
|
|
BinaryOperatorToCarbon(arg0, " <= ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::GreaterEq:
|
|
BinaryOperatorToCarbon(arg0, " >= ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::Greater:
|
|
BinaryOperatorToCarbon(arg0, " > ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Or:
|
|
BinaryOperatorToCarbon(arg0, " or ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::Complement:
|
|
PrefixUnaryOperatorToCarbon("^", arg0, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitwiseAnd:
|
|
BinaryOperatorToCarbon(arg0, " & ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitwiseOr:
|
|
BinaryOperatorToCarbon(arg0, " | ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitwiseXor:
|
|
BinaryOperatorToCarbon(arg0, " ^ ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitShiftLeft:
|
|
BinaryOperatorToCarbon(arg0, " << ", arg1, out);
|
|
break;
|
|
|
|
case Fuzzing::OperatorExpression::BitShiftRight:
|
|
BinaryOperatorToCarbon(arg0, " >> ", arg1, out);
|
|
break;
|
|
case Fuzzing::OperatorExpression::NotEq:
|
|
BinaryOperatorToCarbon(arg0, " != ", arg1, out);
|
|
break;
|
|
}
|
|
out << ")";
|
|
}
|
|
|
|
static auto FieldInitializerToCarbon(const Fuzzing::FieldInitializer& field,
|
|
std::string_view separator,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << ".";
|
|
IdentifierToCarbon(field.name(), out);
|
|
out << " " << separator << " ";
|
|
ExpressionToCarbon(field.expression(), out);
|
|
}
|
|
|
|
static auto TupleLiteralExpressionToCarbon(
|
|
const Fuzzing::TupleLiteralExpression& tuple_literal,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << "(";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& field : tuple_literal.fields()) {
|
|
out << sep;
|
|
ExpressionToCarbon(field, out);
|
|
}
|
|
if (tuple_literal.fields_size() == 1) {
|
|
// Adding a trailing comma so that generated source will be parsed as a
|
|
// tuple expression. See docs/design/tuples.md.
|
|
out << ", ";
|
|
}
|
|
out << ")";
|
|
}
|
|
|
|
static auto ExpressionToCarbon(const Fuzzing::Expression& expression,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (expression.kind_case()) {
|
|
case Fuzzing::Expression::KIND_NOT_SET:
|
|
// Arbitrary default for missing expressions to avoid invalid syntax.
|
|
out << "true";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kCall: {
|
|
const auto& call = expression.call();
|
|
ExpressionToCarbon(call.function(), out);
|
|
if (call.argument().kind_case() == Fuzzing::Expression::kTupleLiteral) {
|
|
TupleLiteralExpressionToCarbon(call.argument().tuple_literal(), out);
|
|
} else {
|
|
out << "(";
|
|
ExpressionToCarbon(call.argument(), out);
|
|
out << ")";
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kFunctionType: {
|
|
const auto& fun_type = expression.function_type();
|
|
out << "__Fn";
|
|
TupleLiteralExpressionToCarbon(fun_type.parameter(), out);
|
|
out << " -> ";
|
|
ExpressionToCarbon(fun_type.return_type(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kSimpleMemberAccess: {
|
|
const auto& simple_member_access = expression.simple_member_access();
|
|
ExpressionToCarbon(simple_member_access.object(), out);
|
|
out << ".";
|
|
IdentifierToCarbon(simple_member_access.field(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kCompoundMemberAccess: {
|
|
const auto& simple_member_access = expression.compound_member_access();
|
|
ExpressionToCarbon(simple_member_access.object(), out);
|
|
out << ".(";
|
|
ExpressionToCarbon(simple_member_access.path(), out);
|
|
out << ")";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kIndex: {
|
|
const auto& index = expression.index();
|
|
ExpressionToCarbon(index.object(), out);
|
|
out << "[";
|
|
ExpressionToCarbon(index.offset(), out);
|
|
out << "]";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kOperator:
|
|
OperatorToCarbon(expression.operator_(), out);
|
|
break;
|
|
|
|
case Fuzzing::Expression::kTupleLiteral: {
|
|
TupleLiteralExpressionToCarbon(expression.tuple_literal(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kStructLiteral: {
|
|
const auto& struct_literal = expression.struct_literal();
|
|
out << "{";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& field : struct_literal.fields()) {
|
|
out << sep;
|
|
FieldInitializerToCarbon(field, "=", out);
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kStructTypeLiteral: {
|
|
const auto& struct_type_literal = expression.struct_type_literal();
|
|
out << "{";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& field : struct_type_literal.fields()) {
|
|
out << sep;
|
|
FieldInitializerToCarbon(field, ":", out);
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kIdentifier: {
|
|
const auto& identifier = expression.identifier();
|
|
IdentifierToCarbon(identifier.name(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kDesignator: {
|
|
const auto& designator = expression.designator();
|
|
out << ".";
|
|
IdentifierToCarbon(designator.name(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kIfExpression: {
|
|
const auto& if_expression = expression.if_expression();
|
|
out << "if ";
|
|
ExpressionToCarbon(if_expression.condition(), out);
|
|
out << " then ";
|
|
ExpressionToCarbon(if_expression.then_expression(), out);
|
|
out << " else ";
|
|
ExpressionToCarbon(if_expression.else_expression(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kBoolTypeLiteral:
|
|
out << "bool";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kBoolLiteral: {
|
|
const auto& bool_literal = expression.bool_literal();
|
|
out << (bool_literal.value() ? "true" : "false");
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kIntTypeLiteral:
|
|
out << "i32";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kIntLiteral: {
|
|
out << expression.int_literal().value();
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kStringLiteral:
|
|
StringLiteralToCarbon(expression.string_literal().value(), out);
|
|
break;
|
|
|
|
case Fuzzing::Expression::kStringTypeLiteral:
|
|
out << "String";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kTypeTypeLiteral:
|
|
out << "type";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kUnimplementedExpression:
|
|
// Not really supported.
|
|
// This is an arbitrary default to avoid getting invalid syntax.
|
|
out << "1 __unimplemented_example_infix 2";
|
|
break;
|
|
|
|
case Fuzzing::Expression::kArrayTypeLiteral: {
|
|
const Fuzzing::ArrayTypeLiteral& array_literal =
|
|
expression.array_type_literal();
|
|
out << "[";
|
|
ExpressionToCarbon(array_literal.element_type(), out);
|
|
out << ";";
|
|
if (array_literal.has_size()) {
|
|
out << " ";
|
|
ExpressionToCarbon(array_literal.size(), out);
|
|
}
|
|
out << "]";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Expression::kWhere: {
|
|
const Fuzzing::WhereExpression& where = expression.where();
|
|
ExpressionToCarbon(where.base(), out);
|
|
out << " where ";
|
|
llvm::ListSeparator sep(" and ");
|
|
for (const auto& clause : where.clauses()) {
|
|
out << sep;
|
|
switch (clause.kind_case()) {
|
|
case Fuzzing::WhereClause::kImpls:
|
|
ExpressionToCarbon(clause.impls().type(), out);
|
|
out << " impls ";
|
|
ExpressionToCarbon(clause.impls().constraint(), out);
|
|
break;
|
|
case Fuzzing::WhereClause::kEquals:
|
|
ExpressionToCarbon(clause.equals().lhs(), out);
|
|
out << " == ";
|
|
ExpressionToCarbon(clause.equals().rhs(), out);
|
|
break;
|
|
case Fuzzing::WhereClause::kRewrite:
|
|
out << "." << clause.rewrite().member_name() << " = ";
|
|
ExpressionToCarbon(clause.rewrite().replacement(), out);
|
|
break;
|
|
case Fuzzing::WhereClause::KIND_NOT_SET:
|
|
// Arbitrary default to avoid invalid syntax.
|
|
out << ".Self == .Self";
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static auto BindingPatternToCarbon(const Fuzzing::BindingPattern& pattern,
|
|
llvm::raw_ostream& out) -> void {
|
|
IdentifierToCarbon(pattern.name(), out);
|
|
out << ": ";
|
|
PatternToCarbon(pattern.type(), out);
|
|
}
|
|
|
|
static auto GenericBindingToCarbon(
|
|
const Fuzzing::GenericBinding& generic_binding, llvm::raw_ostream& out) {
|
|
switch (generic_binding.kind()) {
|
|
case Fuzzing::GenericBinding::Checked:
|
|
break;
|
|
case Fuzzing::GenericBinding::Template:
|
|
out << "template ";
|
|
break;
|
|
}
|
|
IdentifierToCarbon(generic_binding.name(), out);
|
|
out << ":! ";
|
|
ExpressionToCarbon(generic_binding.type(), out);
|
|
}
|
|
|
|
static auto TuplePatternToCarbon(const Fuzzing::TuplePattern& tuple_pattern,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << "(";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& field : tuple_pattern.fields()) {
|
|
out << sep;
|
|
PatternToCarbon(field, out);
|
|
}
|
|
if (tuple_pattern.fields_size() == 1) {
|
|
// Adding a trailing comma so that generated source will be parsed as a
|
|
// tuple pattern expression. See docs/design/tuples.md.
|
|
out << ", ";
|
|
}
|
|
out << ")";
|
|
}
|
|
|
|
static auto PatternToCarbon(const Fuzzing::Pattern& pattern,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (pattern.kind_case()) {
|
|
case Fuzzing::Pattern::KIND_NOT_SET:
|
|
// Arbitrary default to avoid getting invalid syntax.
|
|
out << "auto";
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kBindingPattern:
|
|
BindingPatternToCarbon(pattern.binding_pattern(), out);
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kTuplePattern:
|
|
TuplePatternToCarbon(pattern.tuple_pattern(), out);
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kAlternativePattern: {
|
|
const auto& alternative_pattern = pattern.alternative_pattern();
|
|
ExpressionToCarbon(alternative_pattern.choice_type(), out);
|
|
out << ".";
|
|
IdentifierToCarbon(alternative_pattern.alternative_name(), out);
|
|
TuplePatternToCarbon(alternative_pattern.arguments(), out);
|
|
break;
|
|
}
|
|
|
|
// Arbitrary expression.
|
|
case Fuzzing::Pattern::kExpressionPattern: {
|
|
const auto& expression_pattern = pattern.expression_pattern();
|
|
ExpressionToCarbon(expression_pattern.expression(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Pattern::kAutoPattern:
|
|
out << "auto";
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kVarPattern:
|
|
out << "var ";
|
|
PatternToCarbon(pattern.var_pattern().pattern(), out);
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kGenericBinding:
|
|
GenericBindingToCarbon(pattern.generic_binding(), out);
|
|
break;
|
|
|
|
case Fuzzing::Pattern::kAddrPattern:
|
|
out << "addr ";
|
|
BindingPatternToCarbon(pattern.addr_pattern().binding_pattern(), out);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static auto BlockStatementToCarbon(const Fuzzing::BlockStatement& block,
|
|
llvm::raw_ostream& out) -> void {
|
|
out << "{\n";
|
|
for (const auto& statement : block.statements()) {
|
|
StatementToCarbon(statement, out);
|
|
out << "\n";
|
|
}
|
|
out << "}\n";
|
|
}
|
|
|
|
static auto StatementToCarbon(const Fuzzing::Statement& statement,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (statement.kind_case()) {
|
|
case Fuzzing::Statement::KIND_NOT_SET:
|
|
// Arbitrary default to avoid getting invalid syntax.
|
|
out << "true;\n";
|
|
break;
|
|
|
|
case Fuzzing::Statement::kExpressionStatement: {
|
|
const auto& expression_statement = statement.expression_statement();
|
|
ExpressionToCarbon(expression_statement.expression(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kAssign: {
|
|
const auto& assign_statement = statement.assign();
|
|
ExpressionToCarbon(assign_statement.lhs(), out);
|
|
switch (assign_statement.op()) {
|
|
case Fuzzing::AssignStatement::Plain:
|
|
out << " = ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Add:
|
|
out << " += ";
|
|
break;
|
|
case Fuzzing::AssignStatement::And:
|
|
out << " &= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Div:
|
|
out << " /= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Mod:
|
|
out << " %= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Mul:
|
|
out << " *= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Or:
|
|
out << " |= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::ShiftLeft:
|
|
out << " <<= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::ShiftRight:
|
|
out << " >>= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Sub:
|
|
out << " -= ";
|
|
break;
|
|
case Fuzzing::AssignStatement::Xor:
|
|
out << " ^= ";
|
|
break;
|
|
}
|
|
ExpressionToCarbon(assign_statement.rhs(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kIncDec: {
|
|
const auto& inc_dec_statement = statement.inc_dec();
|
|
out << (inc_dec_statement.is_increment() ? "++" : "--");
|
|
ExpressionToCarbon(inc_dec_statement.operand(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kVariableDefinition: {
|
|
const auto& def = statement.variable_definition();
|
|
if (def.is_returned()) {
|
|
out << "returned ";
|
|
}
|
|
out << "var ";
|
|
PatternToCarbon(def.pattern(), out);
|
|
if (def.has_init()) {
|
|
out << " = ";
|
|
ExpressionToCarbon(def.init(), out);
|
|
}
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kIfStatement: {
|
|
const auto& if_statement = statement.if_statement();
|
|
out << "if (";
|
|
ExpressionToCarbon(if_statement.condition(), out);
|
|
out << ") ";
|
|
BlockStatementToCarbon(if_statement.then_block(), out);
|
|
// `else` is optional.
|
|
if (if_statement.has_else_block()) {
|
|
out << " else ";
|
|
BlockStatementToCarbon(if_statement.else_block(), out);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kReturnVarStatement: {
|
|
out << "return var;";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kReturnExpressionStatement: {
|
|
const auto& ret = statement.return_expression_statement();
|
|
out << "return";
|
|
if (!ret.is_omitted_expression()) {
|
|
out << " ";
|
|
ExpressionToCarbon(ret.expression(), out);
|
|
}
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kBlock:
|
|
BlockStatementToCarbon(statement.block(), out);
|
|
break;
|
|
|
|
case Fuzzing::Statement::kWhileStatement: {
|
|
const auto& while_statement = statement.while_statement();
|
|
out << "while (";
|
|
ExpressionToCarbon(while_statement.condition(), out);
|
|
out << ") ";
|
|
BlockStatementToCarbon(while_statement.body(), out);
|
|
break;
|
|
}
|
|
case Fuzzing::Statement::kForStatement: {
|
|
const auto& for_statement = statement.for_statement();
|
|
out << "for (";
|
|
BindingPatternToCarbon(for_statement.var_decl(), out);
|
|
out << " in ";
|
|
ExpressionToCarbon(for_statement.target(), out);
|
|
out << ") ";
|
|
BlockStatementToCarbon(for_statement.body(), out);
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kMatch: {
|
|
const auto& match = statement.match();
|
|
out << "match (";
|
|
ExpressionToCarbon(match.expression(), out);
|
|
out << ") {";
|
|
for (const auto& clause : match.clauses()) {
|
|
if (clause.is_default()) {
|
|
out << "default";
|
|
} else {
|
|
out << "case ";
|
|
PatternToCarbon(clause.pattern(), out);
|
|
}
|
|
out << " => ";
|
|
StatementToCarbon(clause.statement(), out);
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Statement::kBreakStatement:
|
|
out << "break;";
|
|
break;
|
|
|
|
case Fuzzing::Statement::kContinueStatement:
|
|
out << "continue;";
|
|
break;
|
|
}
|
|
}
|
|
|
|
static auto ReturnTermToCarbon(const Fuzzing::ReturnTerm& return_term,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (return_term.kind()) {
|
|
case Fuzzing::ReturnTerm::UnknownReturnKind:
|
|
case Fuzzing::ReturnTerm::Omitted:
|
|
break;
|
|
case Fuzzing::ReturnTerm::Auto:
|
|
out << " -> auto";
|
|
break;
|
|
case Fuzzing::ReturnTerm::Expression:
|
|
out << " -> ";
|
|
ExpressionToCarbon(return_term.type(), out);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static auto DeclaredNameToCarbon(const Fuzzing::DeclaredName& name,
|
|
llvm::raw_ostream& out) -> void {
|
|
for (const std::string& qual : name.qualifiers()) {
|
|
IdentifierToCarbon(qual, out);
|
|
out << ".";
|
|
}
|
|
IdentifierToCarbon(name.name(), out);
|
|
}
|
|
|
|
static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
|
|
llvm::raw_ostream& out) -> void {
|
|
switch (declaration.kind_case()) {
|
|
case Fuzzing::Declaration::KIND_NOT_SET: {
|
|
// Arbitrary default to avoid getting invalid syntax.
|
|
out << "var x: i32;";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kNamespace: {
|
|
out << "namespace ";
|
|
DeclaredNameToCarbon(declaration.namespace_().name(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kDestructor: {
|
|
const auto& function = declaration.destructor();
|
|
out << "destructor";
|
|
llvm::ListSeparator sep;
|
|
out << "[";
|
|
if (function.has_self_pattern()) {
|
|
// This is a class method.
|
|
out << sep;
|
|
PatternToCarbon(function.self_pattern(), out);
|
|
}
|
|
out << "]";
|
|
|
|
// Body is optional.
|
|
if (function.has_body()) {
|
|
out << "\n";
|
|
BlockStatementToCarbon(function.body(), out);
|
|
} else {
|
|
out << ";";
|
|
}
|
|
break;
|
|
}
|
|
case Fuzzing::Declaration::kFunction: {
|
|
const auto& function = declaration.function();
|
|
out << "fn ";
|
|
DeclaredNameToCarbon(function.name(), out);
|
|
|
|
if (!function.deduced_parameters().empty() ||
|
|
function.has_self_pattern()) {
|
|
out << "[";
|
|
llvm::ListSeparator sep;
|
|
for (const Fuzzing::GenericBinding& p : function.deduced_parameters()) {
|
|
out << sep;
|
|
GenericBindingToCarbon(p, out);
|
|
}
|
|
if (function.has_self_pattern()) {
|
|
// This is a class method.
|
|
out << sep;
|
|
PatternToCarbon(function.self_pattern(), out);
|
|
}
|
|
out << "]";
|
|
}
|
|
TuplePatternToCarbon(function.param_pattern(), out);
|
|
ReturnTermToCarbon(function.return_term(), out);
|
|
|
|
// Body is optional.
|
|
if (function.has_body()) {
|
|
out << "\n";
|
|
BlockStatementToCarbon(function.body(), out);
|
|
} else {
|
|
out << ";";
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kClassDeclaration: {
|
|
const auto& class_declaration = declaration.class_declaration();
|
|
out << "class ";
|
|
DeclaredNameToCarbon(class_declaration.name(), out);
|
|
|
|
// type_params is optional.
|
|
if (class_declaration.has_type_params()) {
|
|
TuplePatternToCarbon(class_declaration.type_params(), out);
|
|
}
|
|
|
|
out << "{\n";
|
|
for (const auto& member : class_declaration.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kExtendBase: {
|
|
const auto& extend_base_declaration = declaration.extend_base();
|
|
out << " extend base: ";
|
|
ExpressionToCarbon(extend_base_declaration.base_class(), out);
|
|
out << ";\n";
|
|
break;
|
|
}
|
|
|
|
// EXPERIMENTAL MIXIN FEATURE
|
|
case Fuzzing::Declaration::kMixin: {
|
|
const auto& mixin_declaration = declaration.mixin();
|
|
out << "__mixin ";
|
|
DeclaredNameToCarbon(mixin_declaration.name(), out);
|
|
|
|
// type params are not implemented yet
|
|
// if (mixin_declaration.has_params()) {
|
|
// TuplePatternToCarbon(mixin_declaration.params(), out);
|
|
//}
|
|
|
|
out << "{\n";
|
|
for (const auto& member : mixin_declaration.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
// TODO: need to handle interface.self()?
|
|
break;
|
|
}
|
|
|
|
// EXPERIMENTAL MIXIN FEATURE
|
|
case Fuzzing::Declaration::kMix: {
|
|
const auto& mix_declaration = declaration.mix();
|
|
out << "__mix ";
|
|
ExpressionToCarbon(mix_declaration.mixin(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kChoice: {
|
|
const auto& choice = declaration.choice();
|
|
out << "choice ";
|
|
DeclaredNameToCarbon(choice.name(), out);
|
|
|
|
out << "{";
|
|
llvm::ListSeparator sep;
|
|
for (const auto& alternative : choice.alternatives()) {
|
|
out << sep;
|
|
IdentifierToCarbon(alternative.name(), out);
|
|
if (alternative.has_signature()) {
|
|
TupleLiteralExpressionToCarbon(alternative.signature(), out);
|
|
}
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kVariable: {
|
|
const auto& var = declaration.variable();
|
|
out << "var ";
|
|
BindingPatternToCarbon(var.binding(), out);
|
|
|
|
// Initializer is optional.
|
|
if (var.has_initializer()) {
|
|
out << " = ";
|
|
ExpressionToCarbon(var.initializer(), out);
|
|
}
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kLet: {
|
|
const auto& let = declaration.let();
|
|
out << "let ";
|
|
PatternToCarbon(let.pattern(), out);
|
|
|
|
// TODO: Print out the initializer once it's supported.
|
|
// if (let.has_initializer()) {
|
|
// out << " = ";
|
|
// ExpressionToCarbon(let.initializer(), out);
|
|
// }
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kInterfaceExtend: {
|
|
const auto& extend = declaration.interface_extend();
|
|
out << "extend ";
|
|
ExpressionToCarbon(extend.base(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kInterfaceRequire: {
|
|
const auto& impl = declaration.interface_require();
|
|
out << "require ";
|
|
ExpressionToCarbon(impl.impl_type(), out);
|
|
out << " impls ";
|
|
ExpressionToCarbon(impl.constraint(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kInterface: {
|
|
const auto& interface = declaration.interface();
|
|
out << "interface ";
|
|
DeclaredNameToCarbon(interface.name(), out);
|
|
out << " {\n";
|
|
for (const auto& member : interface.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kConstraint: {
|
|
const auto& constraint = declaration.constraint();
|
|
out << "constraint ";
|
|
DeclaredNameToCarbon(constraint.name(), out);
|
|
out << " {\n";
|
|
for (const auto& member : constraint.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kImpl: {
|
|
const auto& impl = declaration.impl();
|
|
if (impl.kind() == Fuzzing::ImplDeclaration::InternalImpl) {
|
|
out << "extend ";
|
|
}
|
|
out << "impl ";
|
|
if (!impl.deduced_parameters().empty()) {
|
|
out << "forall [";
|
|
llvm::ListSeparator sep;
|
|
for (const Fuzzing::GenericBinding& p : impl.deduced_parameters()) {
|
|
out << sep;
|
|
GenericBindingToCarbon(p, out);
|
|
}
|
|
out << "] ";
|
|
}
|
|
if (impl.kind() != Fuzzing::ImplDeclaration::InternalImpl) {
|
|
ExpressionToCarbon(impl.impl_type(), out);
|
|
out << " ";
|
|
}
|
|
out << "as ";
|
|
ExpressionToCarbon(impl.interface(), out);
|
|
out << " {\n";
|
|
for (const auto& member : impl.members()) {
|
|
DeclarationToCarbon(member, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kMatchFirst: {
|
|
const auto& match_first = declaration.match_first();
|
|
out << "__match_first {\n";
|
|
for (const auto& impl : match_first.impl_declarations()) {
|
|
DeclarationToCarbon(impl, out);
|
|
out << "\n";
|
|
}
|
|
out << "}";
|
|
break;
|
|
}
|
|
|
|
case Fuzzing::Declaration::kAlias: {
|
|
const auto& alias = declaration.alias();
|
|
out << "alias ";
|
|
DeclaredNameToCarbon(alias.name(), out);
|
|
out << " = ";
|
|
ExpressionToCarbon(alias.target(), out);
|
|
out << ";";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ProtoToCarbon(const Fuzzing::Carbon& proto, bool maybe_add_main)
|
|
-> std::string {
|
|
std::string source;
|
|
llvm::raw_string_ostream out(source);
|
|
|
|
out << "// Generated by proto_to_carbon.\n"
|
|
"package ";
|
|
const auto& unit = proto.compilation_unit();
|
|
LibraryNameToCarbon(unit.package_statement(), out);
|
|
out << (unit.is_api() ? " api" : " impl") << ";\n\n";
|
|
|
|
bool has_main = false;
|
|
if (!unit.declarations().empty()) {
|
|
for (const auto& declaration : unit.declarations()) {
|
|
DeclarationToCarbon(declaration, out);
|
|
if (declaration.kind_case() == Fuzzing::Declaration::kFunction &&
|
|
declaration.function().name().name() == "Main") {
|
|
has_main = true;
|
|
}
|
|
out << "\n";
|
|
}
|
|
}
|
|
|
|
if (maybe_add_main && !has_main) {
|
|
out << "fn Main() -> i32 { return 0; }\n";
|
|
}
|
|
|
|
return source;
|
|
}
|
|
|
|
auto ParseCarbonTextProto(const std::string& contents)
|
|
-> ErrorOr<Fuzzing::Carbon> {
|
|
google::protobuf::TextFormat::Parser parser;
|
|
Fuzzing::Carbon carbon_proto;
|
|
if (!parser.ParseFromString(contents, &carbon_proto)) {
|
|
return ErrorBuilder() << "Couldn't parse Carbon text proto";
|
|
}
|
|
return carbon_proto;
|
|
}
|
|
|
|
} // namespace Carbon
|