blob: e7c472e8e17b686209b3a7318e31e00fdd725e54 [file] [log] [blame] [edit]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package fidlgen_cpp
import (
"fmt"
"strings"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
var zxNs Namespace = NewNamespace("zx")
var fidlNs Namespace = NewNamespace("fidl")
var internalNs Namespace = fidlNs.Append("internal")
// variant controls how we refer to domain object declarations.
type variant string
const (
noVariant variant = ""
naturalVariant variant = "natural"
unifiedVariant variant = "unified"
wireVariant variant = "wire"
)
var currentVariant = noVariant
// Namespace represents a C++ namespace.
type Namespace []string
func NewNamespace(ns string) Namespace {
return Namespace(strings.Split(ns, "::"))
}
// Namespace is implemented to satisfy the Namespaced interface.
func (ns Namespace) Namespace() Namespace {
return ns
}
// String returns the fully qualified namespace including leading ::.
func (ns Namespace) String() string {
if len(ns) == 0 {
return ""
}
return "::" + ns.NoLeading()
}
// NoLeading returns the fully qualified namespace without the leading ::.
func (ns Namespace) NoLeading() string {
return strings.Join(ns, "::")
}
// Append returns a new namespace with an additional component.
func (ns Namespace) Append(part string) Namespace {
newNs := make([]string, len(ns)+1)
copy(newNs, ns)
newNs[len(ns)] = part
return Namespace(newNs)
}
// DropLastComponent returns a new namespace with the final component removed.
func (ns Namespace) DropLastComponent() Namespace {
if len(ns) == 0 {
panic("Can't drop the end of an empty namespace")
}
new := make([]string, len(ns)-1)
copy(new, ns)
return Namespace(new)
}
// Member creates a named declaration within the namespace
func (ns Namespace) Member(name string) Name {
return Name{name: stringNamePart(name), ns: ns}
}
// NameVariants is the name of a type or a template used in the various C++ bindings.
//
// Names are more general than FIDL declarations. All declarations have a corresponding
// type name, but some types are not declared in the generated code (e.g. zx::vmo),
// or are non-nominal (e.g. std::vector<FooBarDecl>).
type NameVariants struct {
Natural Name
// Unified is like Natural, except it consists of type aliases, declared in
// the unified bindings, to natural types. For example, the Natural name
//
// fuchsia::my::lib::FooStruct
//
// would be aliased to the Unified name
//
// fuchsia_my_lib::FooStruct.
//
// Similarly, if Natural is
//
// std::array<std::unique_ptr<fuchsia::my::lib::FooStruct>, 5>
//
// then Unified would use the alias in the template argument:
//
// std::array<std::unique_ptr<fuchsia_my_lib::FooStruct>, 5>.
//
// In case of client and server protocol endpoints, there is no alias,
// and Unified is the same as Natural.
Unified Name
Wire Name
}
// CommonNameVariants returns a NameVariants with the same Name for both Wire and Natural variants.
func CommonNameVariants(decl Name) NameVariants {
return NameVariants{
Natural: decl,
Unified: decl,
Wire: decl,
}
}
func (dn NameVariants) String() string {
switch currentVariant {
case noVariant:
fidlgen.TemplateFatalf("Called NameVariants.String() on %s/%s when currentVariant isn't set.\n",
dn.Natural, dn.Wire)
case naturalVariant:
return dn.Natural.String()
case unifiedVariant:
return dn.Unified.String()
case wireVariant:
return dn.Wire.String()
}
panic("not reached")
}
func (dn NameVariants) Name() string {
switch currentVariant {
case noVariant:
fidlgen.TemplateFatalf("Called NameVariants.Name() on %s/%s when currentVariant isn't set.\n",
dn.Natural, dn.Wire)
case naturalVariant:
return dn.Natural.Name()
case unifiedVariant:
return dn.Unified.Name()
case wireVariant:
return dn.Wire.Name()
}
panic("not reached")
}
func (dn NameVariants) Self() string {
switch currentVariant {
case noVariant:
fidlgen.TemplateFatalf("Called NameVariants.Self() on %s/%s when currentVariant isn't set.\n", dn.Natural, dn.Wire)
case naturalVariant:
return dn.Natural.Self()
case wireVariant:
return dn.Wire.Self()
}
panic("not reached")
}
func (dn NameVariants) Namespace() Namespace {
switch currentVariant {
case noVariant:
fidlgen.TemplateFatalf("Called NameVariants.Namespace() on %s/%s when currentVariant isn't set.\n",
dn.Natural, dn.Wire)
case naturalVariant:
return dn.Natural.Namespace()
case unifiedVariant:
return dn.Unified.Namespace()
case wireVariant:
return dn.Wire.Namespace()
}
panic("not reached")
}
// AppendName returns a new NameVariants with an suffix appended to the name portions.
func (dn NameVariants) AppendName(suffix string) NameVariants {
return NameVariants{
Natural: dn.Natural.AppendName(suffix),
Unified: dn.Unified.AppendName(suffix),
Wire: dn.Wire.AppendName(suffix),
}
}
// PrependName returns a new NameVariants with an prefix prepended to the name portions.
func (dn NameVariants) PrependName(prefix string) NameVariants {
return NameVariants{
Natural: dn.Natural.PrependName(prefix),
Unified: dn.Unified.PrependName(prefix),
Wire: dn.Wire.PrependName(prefix),
}
}
// AppendNamespace returns a new NameVariants with additional C++ namespace components appended.
func (dn NameVariants) AppendNamespace(c string) NameVariants {
return NameVariants{
Natural: dn.Natural.AppendNamespace(c),
Unified: dn.Unified.AppendNamespace(c),
Wire: dn.Wire.AppendNamespace(c),
}
}
// Nest returns a new name for a class nested inside the existing name.
func (dn NameVariants) Nest(c string) NameVariants {
return NameVariants{
Natural: dn.Natural.Nest(c),
Wire: dn.Wire.Nest(c),
}
}
// NameVariantsForHandle returns the C++ name for a handle type
func NameVariantsForHandle(t fidlgen.HandleSubtype) NameVariants {
return CommonNameVariants(zxNs.Member(string(t)))
}
// PrimitiveNameVariants returns a NameVariants for a primitive type, common across all bindings.
func PrimitiveNameVariants(primitive string) NameVariants {
return CommonNameVariants(MakeName(primitive))
}
// namePart represents part of non-namespace part of a name.
// It's implemented by three types: stringNamePart, nestedNamePart and templateNamePart.
// These form a tree to hold the structure of a name so that it can be accessed and manipulated safely.
// For example in fidl::WireInterface<fuchsia_library::Protocol>::ProtocolMethod this would be:
// WireInterface<fuchsia_library::Protocol>::ProtocolMethod
// |-nestedNP---------------------------------------------|
// |-templateNP---------------------------| |-stringNP---|
// |-stringNP---|-Name--------------------|
// TODO(ianloic): rename to idPart
type namePart interface {
// String returns the full name.
String() string
// Self returns how the type refers to itself, like in constructor & destructor names.
// For a nested name like "Foo::Bar::Baz" this would be "Baz".
// For a template name like "Foo::Bar<Baz>" this would be "Bar".
Self() string
// Nest returns a new name for a class nested inside the existing name.
Nest(name string) namePart
// Template returns a new name with this name being an template applied to the |args|.
Template(args string) namePart
// PrependName returns a new name with a prefix prepended.
PrependName(prefix string) namePart
// AppendName returns a new name with a suffix appended.
AppendName(suffix string) namePart
}
type stringNamePart string
var _ namePart = (*stringNamePart)(nil)
func (n stringNamePart) String() string {
return string(n)
}
func (n stringNamePart) Self() string {
return string(n)
}
func (n stringNamePart) Nest(name string) namePart {
return newNestedNamePart(n, stringNamePart(name))
}
func (n stringNamePart) Template(args string) namePart {
return newTemplateNamePart(n, args)
}
func (n stringNamePart) PrependName(prefix string) namePart {
return stringNamePart(prefix + string(n))
}
func (n stringNamePart) AppendName(suffix string) namePart {
return stringNamePart(string(n) + suffix)
}
type nestedNamePart struct {
left namePart
right namePart
}
var _ namePart = (*nestedNamePart)(nil)
func newNestedNamePart(left, right namePart) namePart {
return nestedNamePart{left, right}
}
func (n nestedNamePart) String() string {
return fmt.Sprintf("%s::%s", n.left, n.right)
}
func (n nestedNamePart) Self() string {
return n.right.Self()
}
func (n nestedNamePart) Nest(name string) namePart {
return nestedNamePart{n.left, n.right.Nest(name)}
}
func (n nestedNamePart) Template(args string) namePart {
return newTemplateNamePart(n, args)
}
func (n nestedNamePart) PrependName(prefix string) namePart {
return nestedNamePart{n.left, n.right.PrependName(prefix)}
}
func (n nestedNamePart) AppendName(suffix string) namePart {
return nestedNamePart{n.left, n.right.AppendName(suffix)}
}
type templateNamePart struct {
template namePart
args string
}
var _ namePart = (*templateNamePart)(nil)
func newTemplateNamePart(template namePart, args string) namePart {
return templateNamePart{template, args}
}
func (n templateNamePart) String() string {
return fmt.Sprintf("%s<%s>", n.template, n.args)
}
func (n templateNamePart) Self() string {
return n.template.Self()
}
func (n templateNamePart) Nest(name string) namePart {
return nestedNamePart{n, stringNamePart(name)}
}
func (n templateNamePart) Template(args string) namePart {
panic(fmt.Sprintf("Can't make a template of a template: %s", n))
}
func (n templateNamePart) PrependName(prefix string) namePart {
panic(fmt.Sprintf("Can't prepend to the name of a template: %s", n))
}
func (n templateNamePart) AppendName(suffix string) namePart {
panic(fmt.Sprintf("Can't append to the name of a template: %s", n))
}
// Name holds a C++ qualified identifier.
// See: https://en.cppreference.com/w/cpp/language/identifiers#Qualified_identifiers
// It consists of a Namespace and a namePart.
// TODO(ianloic): move this to the top of the file since it's the most important type.
type Name struct {
name namePart
ns Namespace
}
// MakeName takes a string with a :: separated name and makes a Name treating the last component
// as the local name and the preceding components as the namespace.
// This should only be used with string literals for creating well-known, simple names.
func MakeName(name string) Name {
i := strings.LastIndex(name, "::")
if i == -1 {
return Name{name: stringNamePart(name)}
}
if i == 0 {
panic(fmt.Sprintf("Don't call MakeName with leading double-colons: %v", name))
}
return Name{
name: stringNamePart(name[i+2:]),
ns: NewNamespace(name[0:i]),
}
}
// String returns the full name with a leading :: if the name has a namespace.
func (n Name) String() string {
ns := n.ns.String()
if len(ns) > 0 {
ns = ns + "::"
}
return ns + n.name.String()
}
// Name returns the portion of the name that comes after the namespace.
func (n Name) Name() string {
return n.name.String()
}
// Self returns how the type refers to itself, like in constructor & destructor names.
// For a nested name like "Foo::Bar::Baz" this would be "Baz".
// For a template name like "Foo::Bar<Baz>" this would be "Bar".
func (n Name) Self() string {
return n.name.Self()
}
// TODO(ianloic): probably make this the default
func (n Name) NoLeading() string {
ns := n.ns.NoLeading()
if len(ns) > 0 {
ns = ns + "::"
}
return ns + n.name.String()
}
// Namespace returns the namespace portion of the name.
func (n Name) Namespace() Namespace {
return n.ns
}
// Nest returns a new name for a class nested inside the existing name.
func (n Name) Nest(name string) Name {
return Name{name: n.name.Nest(name), ns: n.ns}
}
// Template returns a new name with this name being an template applied to the |arg|.
func (n Name) Template(arg Name) Name {
return Name{name: n.name.Template(arg.String()), ns: n.ns}
}
// ArrayTemplate returns a new name with this name being an template applied to the |arg| with a |count|.
func (n Name) ArrayTemplate(arg Name, count int) Name {
return Name{name: n.name.Template(fmt.Sprintf("%s, %d", arg.String(), count)), ns: n.ns}
}
// PrependName returns a new name with a prefix prepended to the last part of the name.
func (n Name) PrependName(prefix string) Name {
return Name{name: n.name.PrependName(prefix), ns: n.ns}
}
// AppendName returns a new name with a suffix appended to the last part of the name.
func (n Name) AppendName(suffix string) Name {
return Name{name: n.name.AppendName(suffix), ns: n.ns}
}
// AppendNamespace returns a new name with an additional namespace component added.
func (n Name) AppendNamespace(part string) Name {
return Name{name: n.name, ns: n.ns.Append(part)}
}
// MakeTupleName returns a Name for a std::tuple of the supplied names.
func MakeTupleName(members []Name) Name {
t := MakeName("std::tuple")
a := []string{}
for _, m := range members {
a = append(a, m.String())
}
return Name{name: t.name.Template(strings.Join(a, ", ")), ns: t.ns}
}