blob: 73808ab5a4817abe14c96342d2c99ae923f736fa [file] [log] [blame]
// 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"
)
//
// Combined logic for constant values and const declarations.
//
type ConstantValue struct {
Natural string
Wire string
}
func (cv ConstantValue) IsSet() bool {
return cv.Natural != "" && cv.Wire != ""
}
func (cv ConstantValue) String() string {
switch currentVariant {
case noVariant:
fidlgen.TemplateFatalf(
"Called ConstantValue.String() on %s/%s when currentVariant isn't set.\n",
cv.Natural, cv.Wire)
case naturalVariant:
return string(cv.Natural)
case wireVariant:
return string(cv.Wire)
case unifiedVariant:
return string(cv.Natural)
}
panic("not reached")
}
func (c *compiler) compileConstant(val fidlgen.Constant, t *Type, typ fidlgen.Type) ConstantValue {
switch val.Kind {
case fidlgen.IdentifierConstant:
n := c.compileNameVariants(val.Identifier)
return ConstantValue{Natural: n.Natural.String(), Wire: n.Wire.String()}
case fidlgen.LiteralConstant:
lit := c.compileLiteral(val.Literal, typ)
return ConstantValue{Natural: lit, Wire: lit}
case fidlgen.BinaryOperator:
return ConstantValue{
Natural: fmt.Sprintf("static_cast<%s>(%s)", t.nameVariants.Natural, val.Value),
Wire: fmt.Sprintf("static_cast<%s>(%s)", t.nameVariants.Wire, val.Value),
}
// return ConstantValue{Natural: naturalVal, Wire: wireVal}
default:
panic(fmt.Sprintf("unknown constant kind: %v", val.Kind))
}
}
type Const struct {
Attributes
nameVariants
Extern bool
Decorator string
Type Type
Value ConstantValue
}
func (Const) Kind() declKind {
return Kinds.Const
}
var _ Kinded = (*Const)(nil)
var _ namespaced = (*Const)(nil)
func (c *compiler) compileConst(val fidlgen.Const) Const {
n := c.compileNameVariants(val.Name)
v := Const{
Attributes: Attributes{val.Attributes},
nameVariants: n,
}
if val.Type.Kind == fidlgen.StringType {
v.Extern = true
v.Decorator = "const"
v.Type = Type{
nameVariants: primitiveNameVariants("char*"),
}
v.Value = c.compileConstant(val.Value, nil, val.Type)
} else {
t := c.compileType(val.Type)
v.Extern = false
v.Decorator = "constexpr"
v.Type = t
v.Value = c.compileConstant(val.Value, &t, val.Type)
}
return v
}
func (c *compiler) compileLiteral(val fidlgen.Literal, typ fidlgen.Type) string {
switch val.Kind {
case fidlgen.StringLiteral:
return fmt.Sprintf("%q", val.Value)
case fidlgen.NumericLiteral:
if val.Value == "-9223372036854775808" || val.Value == "0x8000000000000000" {
// C++ only supports nonnegative literals and a value this large in absolute
// value cannot be represented as a nonnegative number in 64-bits.
return "(-9223372036854775807ll-1)"
}
// TODO(fxbug.dev/7810): Once we expose resolved constants for defaults, e.g.
// in structs, we will not need ignore hex and binary values.
if strings.HasPrefix(val.Value, "0x") || strings.HasPrefix(val.Value, "0b") {
return val.Value
}
// float32 literals must be marked as such.
if strings.ContainsRune(val.Value, '.') {
if typ.Kind == fidlgen.PrimitiveType && typ.PrimitiveSubtype == fidlgen.Float32 {
return fmt.Sprintf("%sf", val.Value)
} else {
return val.Value
}
}
if !strings.HasPrefix(val.Value, "-") {
return fmt.Sprintf("%su", val.Value)
}
return val.Value
case fidlgen.TrueLiteral:
return "true"
case fidlgen.FalseLiteral:
return "false"
case fidlgen.DefaultLiteral:
return "default"
default:
panic(fmt.Sprintf("unknown literal kind: %v", val.Kind))
}
}