Files
carbon-lang/common/fuzzing/proto_to_carbon.cpp
T
Jon Ross-Perkins d12583fc08 Do cleanup on explorer fuzzing infrastructure. (#2790)
I'm partly doing this because the current setup would be difficult to share with the toolchain. e.g., ProtoToCarbon isn't explorer-specific, but the only way to run it via CLI is the explorer's fuzzverter. I want a separate tool.

This change:

- Adds a //common/fuzzing:proto_to_carbon tool.
  - The rest of fuzzverter is now just //explorer/fuzzing:ast_to_proto.
  - The change simplifies overall handling and removes a LLVM CLI dependency.
- Stops allowing unknown fields in the proto.
  - This has mostly led to forgetting to remove fuzzer inputs that were for removed features.
- Moves more non-explorer-specific bits to //common/fuzzing.
- Cleans up remaining pieces in //explorer/fuzzing
  - Merges the //explorer/fuzzing proto tests, which deduplicates AstToString copies.
    - These tests also had duplicate dependencies, etc -- and all complete in ~6s.
  - Updates and fixes regen_corpus which was previously broken by other changes.
  - Updates the README to reflect changes.
- Removes obsolete proto-fuzzer build configuration (AFAICT this is no longer needed).
2023-04-21 08:23:58 -07:00

1012 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 "common/fuzzing/proto_to_carbon.h"
#include <google/protobuf/text_format.h>
#include <string_view>
#include "common/fuzzing/carbon.pb.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.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 << "; ";
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;
}
// 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::kInterfaceExtends: {
const auto& extends = declaration.interface_extends();
out << "extends ";
ExpressionToCarbon(extends.base(), out);
out << ";";
break;
}
case Fuzzing::Declaration::kInterfaceImpl: {
const auto& impl = declaration.interface_impl();
out << "impl ";
ExpressionToCarbon(impl.impl_type(), out);
out << " as ";
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::ExternalImpl) {
out << "external ";
}
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 << "]";
}
ExpressionToCarbon(impl.impl_type(), 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