Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
735 lines
16 KiB
Go
735 lines
16 KiB
Go
package schemadsl
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
dmt "github.com/ipld/go-ipld-prime/schema/dmt"
|
|
)
|
|
|
|
var globalTrue = true
|
|
|
|
// TODO: fuzz testing
|
|
|
|
func ParseBytes(src []byte) (*dmt.Schema, error) {
|
|
return Parse("", bytes.NewReader(src))
|
|
}
|
|
|
|
func ParseFile(path string) (*dmt.Schema, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
return Parse(path, f)
|
|
}
|
|
|
|
func Parse(name string, r io.Reader) (*dmt.Schema, error) {
|
|
p := &parser{
|
|
path: name,
|
|
br: bufio.NewReader(r),
|
|
line: 1,
|
|
col: 1,
|
|
}
|
|
|
|
sch := &dmt.Schema{}
|
|
sch.Types.Values = make(map[string]dmt.TypeDefn)
|
|
|
|
for {
|
|
tok, err := p.consumeToken()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
|
|
switch tok {
|
|
case "type":
|
|
name, err := p.consumeName()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defn, err := p.typeDefn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mapAppend(&sch.Types, name, defn)
|
|
case "advanced":
|
|
return nil, p.errf("TODO: advanced")
|
|
default:
|
|
return nil, p.errf("unexpected token: %q", tok)
|
|
}
|
|
}
|
|
return sch, nil
|
|
}
|
|
|
|
func mapAppend(mapPtr, k, v interface{}) {
|
|
// TODO: delete with generics
|
|
// TODO: error on dupes
|
|
|
|
mval := reflect.ValueOf(mapPtr).Elem()
|
|
kval := reflect.ValueOf(k)
|
|
vval := reflect.ValueOf(v)
|
|
|
|
keys := mval.FieldByName("Keys")
|
|
keys.Set(reflect.Append(keys, kval))
|
|
|
|
values := mval.FieldByName("Values")
|
|
if values.IsNil() {
|
|
values.Set(reflect.MakeMap(values.Type()))
|
|
}
|
|
values.SetMapIndex(kval, vval)
|
|
}
|
|
|
|
type parser struct {
|
|
path string
|
|
br *bufio.Reader
|
|
|
|
peekedToken string
|
|
|
|
line, col int
|
|
}
|
|
|
|
func (p *parser) forwardError(err error) error {
|
|
var prefix string
|
|
if p.path != "" {
|
|
prefix = p.path + ":"
|
|
}
|
|
return fmt.Errorf("%s%d:%d: %s", prefix, p.line, p.col, err)
|
|
}
|
|
|
|
func (p *parser) errf(format string, args ...interface{}) error {
|
|
return p.forwardError(fmt.Errorf(format, args...))
|
|
}
|
|
|
|
func (p *parser) consumeToken() (string, error) {
|
|
if tok := p.peekedToken; tok != "" {
|
|
p.peekedToken = ""
|
|
return tok, nil
|
|
}
|
|
for {
|
|
// TODO: use runes for better unicode support
|
|
b, err := p.br.ReadByte()
|
|
if err == io.EOF {
|
|
return "", err // TODO: ErrUnexpectedEOF?
|
|
}
|
|
if err != nil {
|
|
return "", p.forwardError(err)
|
|
}
|
|
p.col++
|
|
switch b {
|
|
case ' ', '\t', '\r': // skip whitespace
|
|
continue
|
|
case '\n': // skip newline
|
|
// TODO: should we require a newline after each type def, struct field, etc?
|
|
p.line++
|
|
p.col = 1
|
|
continue
|
|
case '"': // quoted string
|
|
quoted, err := p.br.ReadString('"')
|
|
if err != nil {
|
|
return "", p.forwardError(err)
|
|
}
|
|
return "\"" + quoted, nil
|
|
case '{', '}', '[', ']', '(', ')', ':', '&': // simple token
|
|
return string(b), nil
|
|
case '#': // comment
|
|
_, err := p.br.ReadString('\n')
|
|
if err != nil {
|
|
return "", p.forwardError(err)
|
|
}
|
|
// tokenize the newline
|
|
if err := p.br.UnreadByte(); err != nil {
|
|
panic(err) // should never happen
|
|
}
|
|
continue
|
|
default: // string token or name
|
|
var sb strings.Builder
|
|
sb.WriteByte(b)
|
|
for {
|
|
b, err := p.br.ReadByte()
|
|
if err == io.EOF {
|
|
// Token ends at the end of the whole input.
|
|
return sb.String(), nil
|
|
}
|
|
if err != nil {
|
|
return "", p.forwardError(err)
|
|
}
|
|
// TODO: should probably allow unicode letters and numbers, like Go?
|
|
switch {
|
|
case b >= 'a' && b <= 'z', b >= 'A' && b <= 'Z':
|
|
case b >= '0' && b <= '9':
|
|
case b == '_':
|
|
default:
|
|
if err := p.br.UnreadByte(); err != nil {
|
|
panic(err) // should never happen
|
|
}
|
|
return sb.String(), nil
|
|
}
|
|
sb.WriteByte(b)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *parser) consumePeeked() {
|
|
if p.peekedToken == "" {
|
|
panic("consumePeeked requires a peeked token to be present")
|
|
}
|
|
p.peekedToken = ""
|
|
}
|
|
|
|
func (p *parser) peekToken() (string, error) {
|
|
if tok := p.peekedToken; tok != "" {
|
|
return tok, nil
|
|
}
|
|
tok, err := p.consumeToken()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
// peekToken is often used when a token is optional.
|
|
// If we hit io.EOF, that's not an error.
|
|
// TODO: consider making peekToken just not return an error?
|
|
return "", nil
|
|
}
|
|
return "", err
|
|
}
|
|
p.peekedToken = tok
|
|
return tok, nil
|
|
}
|
|
|
|
func (p *parser) consumeName() (string, error) {
|
|
tok, err := p.consumeToken()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
switch tok {
|
|
case "\"", "{", "}", "[", "]", "(", ")", ":":
|
|
return "", p.errf("expected a name, got %q", tok)
|
|
}
|
|
if tok[0] == '"' {
|
|
return "", p.errf("expected a name, got string %s", tok)
|
|
}
|
|
return tok, nil
|
|
}
|
|
|
|
func (p *parser) consumeString() (string, error) {
|
|
tok, err := p.consumeToken()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if tok[0] != '"' {
|
|
return "", p.errf("expected a string, got %q", tok)
|
|
}
|
|
// Unquote, too.
|
|
return tok[1 : len(tok)-1], nil
|
|
}
|
|
|
|
func (p *parser) consumeStringMap() (map[string]string, error) {
|
|
result := map[string]string{}
|
|
loop:
|
|
for {
|
|
tok, err := p.peekToken()
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
switch tok {
|
|
case "{":
|
|
p.consumePeeked()
|
|
case "}":
|
|
p.consumePeeked()
|
|
break loop
|
|
default:
|
|
key, err := p.consumeName()
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
value, err := p.consumeString()
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
result[key] = value
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (p *parser) consumeRequired(tok string) error {
|
|
got, err := p.consumeToken()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if got != tok {
|
|
return p.errf("expected %q, got %q", tok, got)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *parser) typeDefn() (dmt.TypeDefn, error) {
|
|
var defn dmt.TypeDefn
|
|
kind, err := p.consumeToken()
|
|
if err != nil {
|
|
return defn, err
|
|
}
|
|
|
|
switch kind {
|
|
case "struct":
|
|
if err := p.consumeRequired("{"); err != nil {
|
|
return defn, err
|
|
}
|
|
defn.TypeDefnStruct, err = p.typeStruct()
|
|
case "union":
|
|
if err := p.consumeRequired("{"); err != nil {
|
|
return defn, err
|
|
}
|
|
defn.TypeDefnUnion, err = p.typeUnion()
|
|
case "enum":
|
|
if err := p.consumeRequired("{"); err != nil {
|
|
return defn, err
|
|
}
|
|
defn.TypeDefnEnum, err = p.typeEnum()
|
|
case "bool":
|
|
defn.TypeDefnBool = &dmt.TypeDefnBool{}
|
|
case "bytes":
|
|
defn.TypeDefnBytes = &dmt.TypeDefnBytes{}
|
|
case "float":
|
|
defn.TypeDefnFloat = &dmt.TypeDefnFloat{}
|
|
case "int":
|
|
defn.TypeDefnInt = &dmt.TypeDefnInt{}
|
|
case "link":
|
|
defn.TypeDefnLink = &dmt.TypeDefnLink{}
|
|
case "any":
|
|
defn.TypeDefnAny = &dmt.TypeDefnAny{}
|
|
case "&":
|
|
target, err := p.consumeName()
|
|
if err != nil {
|
|
return defn, err
|
|
}
|
|
defn.TypeDefnLink = &dmt.TypeDefnLink{ExpectedType: &target}
|
|
case "string":
|
|
defn.TypeDefnString = &dmt.TypeDefnString{}
|
|
case "{":
|
|
defn.TypeDefnMap, err = p.typeMap()
|
|
case "[":
|
|
defn.TypeDefnList, err = p.typeList()
|
|
case "=":
|
|
from, err := p.consumeName()
|
|
if err != nil {
|
|
return defn, err
|
|
}
|
|
defn.TypeDefnCopy = &dmt.TypeDefnCopy{FromType: from}
|
|
default:
|
|
err = p.errf("unknown type keyword: %q", kind)
|
|
}
|
|
|
|
return defn, err
|
|
}
|
|
|
|
func (p *parser) typeStruct() (*dmt.TypeDefnStruct, error) {
|
|
repr := &dmt.StructRepresentation_Map{}
|
|
repr.Fields = &dmt.Map__FieldName__StructRepresentation_Map_FieldDetails{}
|
|
|
|
defn := &dmt.TypeDefnStruct{}
|
|
for {
|
|
tok, err := p.consumeToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if tok == "}" {
|
|
break
|
|
}
|
|
name := tok
|
|
|
|
var field dmt.StructField
|
|
loop:
|
|
for {
|
|
tok, err := p.peekToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch tok {
|
|
case "optional":
|
|
if field.Optional != nil {
|
|
return nil, p.errf("multiple optional keywords")
|
|
}
|
|
field.Optional = &globalTrue
|
|
p.consumePeeked()
|
|
case "nullable":
|
|
if field.Nullable != nil {
|
|
return nil, p.errf("multiple nullable keywords")
|
|
}
|
|
field.Nullable = &globalTrue
|
|
p.consumePeeked()
|
|
default:
|
|
var err error
|
|
field.Type, err = p.typeNameOrInlineDefn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
break loop
|
|
}
|
|
}
|
|
tok, err = p.peekToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tok == "(" {
|
|
details := dmt.StructRepresentation_Map_FieldDetails{}
|
|
p.consumePeeked()
|
|
parenLoop:
|
|
for {
|
|
tok, err = p.consumeToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch tok {
|
|
case ")":
|
|
break parenLoop
|
|
case "rename":
|
|
str, err := p.consumeString()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
details.Rename = &str
|
|
case "implicit":
|
|
scalar, err := p.consumeToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var anyScalar dmt.AnyScalar
|
|
switch {
|
|
case scalar[0] == '"': // string
|
|
s, err := strconv.Unquote(scalar)
|
|
if err != nil {
|
|
return nil, p.forwardError(err)
|
|
}
|
|
anyScalar.String = &s
|
|
case scalar == "true", scalar == "false": // bool
|
|
t := scalar == "true"
|
|
anyScalar.Bool = &t
|
|
case scalar[0] >= '0' && scalar[0] <= '0':
|
|
n, err := strconv.Atoi(scalar)
|
|
if err != nil {
|
|
return nil, p.forwardError(err)
|
|
}
|
|
anyScalar.Int = &n
|
|
default:
|
|
return nil, p.errf("unsupported implicit scalar: %s", scalar)
|
|
}
|
|
|
|
details.Implicit = &anyScalar
|
|
}
|
|
}
|
|
mapAppend(repr.Fields, name, details)
|
|
}
|
|
|
|
mapAppend(&defn.Fields, name, field)
|
|
}
|
|
|
|
reprName := "map" // default repr
|
|
if tok, err := p.peekToken(); err == nil && tok == "representation" {
|
|
p.consumePeeked()
|
|
name, err := p.consumeName()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reprName = name
|
|
}
|
|
if reprName != "map" && len(repr.Fields.Keys) > 0 {
|
|
return nil, p.errf("rename and implicit are only supported for struct map representations")
|
|
}
|
|
switch reprName {
|
|
case "map":
|
|
if len(repr.Fields.Keys) == 0 {
|
|
// Fields is optional; omit it if empty.
|
|
repr.Fields = nil
|
|
}
|
|
defn.Representation.StructRepresentation_Map = repr
|
|
return defn, nil
|
|
case "tuple":
|
|
defn.Representation.StructRepresentation_Tuple = &dmt.StructRepresentation_Tuple{}
|
|
return defn, nil
|
|
// TODO: support custom fieldorder
|
|
case "stringjoin":
|
|
optMap, err := p.consumeStringMap()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
join, hasJoin := optMap["join"]
|
|
if !hasJoin {
|
|
return nil, p.errf("no join value provided for stringjoin repr")
|
|
}
|
|
defn.Representation.StructRepresentation_Stringjoin = &dmt.StructRepresentation_Stringjoin{
|
|
Join: join,
|
|
}
|
|
return defn, nil
|
|
default:
|
|
return nil, p.errf("unknown struct repr: %q", reprName)
|
|
}
|
|
}
|
|
|
|
func (p *parser) typeNameOrInlineDefn() (dmt.TypeNameOrInlineDefn, error) {
|
|
var typ dmt.TypeNameOrInlineDefn
|
|
tok, err := p.consumeToken()
|
|
if err != nil {
|
|
return typ, err
|
|
}
|
|
|
|
switch tok {
|
|
case "&":
|
|
expectedName, err := p.consumeName()
|
|
if err != nil {
|
|
return typ, err
|
|
}
|
|
typ.InlineDefn = &dmt.InlineDefn{TypeDefnLink: &dmt.TypeDefnLink{ExpectedType: &expectedName}}
|
|
case "[":
|
|
tlist, err := p.typeList()
|
|
if err != nil {
|
|
return typ, err
|
|
}
|
|
typ.InlineDefn = &dmt.InlineDefn{TypeDefnList: tlist}
|
|
case "{":
|
|
tmap, err := p.typeMap()
|
|
if err != nil {
|
|
return typ, err
|
|
}
|
|
typ.InlineDefn = &dmt.InlineDefn{TypeDefnMap: tmap}
|
|
default:
|
|
typ.TypeName = &tok
|
|
}
|
|
return typ, nil
|
|
}
|
|
|
|
func (p *parser) typeList() (*dmt.TypeDefnList, error) {
|
|
defn := &dmt.TypeDefnList{}
|
|
tok, err := p.peekToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tok == "nullable" {
|
|
defn.ValueNullable = &globalTrue
|
|
p.consumePeeked()
|
|
}
|
|
|
|
defn.ValueType, err = p.typeNameOrInlineDefn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.consumeRequired("]"); err != nil {
|
|
return defn, err
|
|
}
|
|
|
|
// TODO: repr
|
|
return defn, nil
|
|
}
|
|
|
|
func (p *parser) typeMap() (*dmt.TypeDefnMap, error) {
|
|
defn := &dmt.TypeDefnMap{}
|
|
|
|
var err error
|
|
defn.KeyType, err = p.consumeName()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := p.consumeRequired(":"); err != nil {
|
|
return defn, err
|
|
}
|
|
|
|
tok, err := p.peekToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tok == "nullable" {
|
|
defn.ValueNullable = &globalTrue
|
|
p.consumePeeked()
|
|
}
|
|
|
|
defn.ValueType, err = p.typeNameOrInlineDefn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.consumeRequired("}"); err != nil {
|
|
return defn, err
|
|
}
|
|
|
|
return defn, nil
|
|
}
|
|
|
|
func (p *parser) typeUnion() (*dmt.TypeDefnUnion, error) {
|
|
defn := &dmt.TypeDefnUnion{}
|
|
var reprKeys []string
|
|
|
|
for {
|
|
tok, err := p.consumeToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tok == "}" {
|
|
break
|
|
}
|
|
if tok != "|" {
|
|
return nil, p.errf("expected %q or %q, got %q", "}", "|", tok)
|
|
}
|
|
var member dmt.UnionMember
|
|
nameOrInline, err := p.typeNameOrInlineDefn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if nameOrInline.TypeName != nil {
|
|
member.TypeName = nameOrInline.TypeName
|
|
} else {
|
|
if nameOrInline.InlineDefn.TypeDefnLink != nil {
|
|
member.UnionMemberInlineDefn = &dmt.UnionMemberInlineDefn{TypeDefnLink: nameOrInline.InlineDefn.TypeDefnLink}
|
|
} else {
|
|
return nil, p.errf("expected a name or inline link, got neither")
|
|
}
|
|
}
|
|
defn.Members = append(defn.Members, member)
|
|
|
|
key, err := p.consumeToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reprKeys = append(reprKeys, key)
|
|
}
|
|
if err := p.consumeRequired("representation"); err != nil {
|
|
return nil, err
|
|
}
|
|
reprName, err := p.consumeName()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch reprName {
|
|
case "keyed":
|
|
repr := &dmt.UnionRepresentation_Keyed{}
|
|
for i, keyStr := range reprKeys {
|
|
key, err := strconv.Unquote(keyStr)
|
|
if err != nil {
|
|
return nil, p.forwardError(err)
|
|
}
|
|
mapAppend(repr, key, defn.Members[i])
|
|
}
|
|
defn.Representation.UnionRepresentation_Keyed = repr
|
|
case "kinded":
|
|
repr := &dmt.UnionRepresentation_Kinded{}
|
|
// TODO: verify keys are valid kinds? enum should do it for us?
|
|
for i, key := range reprKeys {
|
|
mapAppend(repr, key, defn.Members[i])
|
|
}
|
|
defn.Representation.UnionRepresentation_Kinded = repr
|
|
case "stringprefix":
|
|
repr := &dmt.UnionRepresentation_StringPrefix{
|
|
Prefixes: dmt.Map__String__TypeName{
|
|
Values: map[string]string{},
|
|
},
|
|
}
|
|
for i, key := range reprKeys {
|
|
// unquote prefix string
|
|
if len(key) < 2 || key[0] != '"' || key[len(key)-1] != '"' {
|
|
return nil, p.errf("invalid stringprefix %q", key)
|
|
}
|
|
key = key[1 : len(key)-1]
|
|
|
|
// add prefix to prefixes map
|
|
repr.Prefixes.Keys = append(repr.Prefixes.Keys, key)
|
|
repr.Prefixes.Values[key] = *defn.Members[i].TypeName
|
|
}
|
|
defn.Representation.UnionRepresentation_StringPrefix = repr
|
|
default:
|
|
return nil, p.errf("TODO: union repr %q", reprName)
|
|
}
|
|
return defn, nil
|
|
}
|
|
|
|
func (p *parser) typeEnum() (*dmt.TypeDefnEnum, error) {
|
|
defn := &dmt.TypeDefnEnum{}
|
|
var reprKeys []string
|
|
|
|
for {
|
|
tok, err := p.consumeToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tok == "}" {
|
|
break
|
|
}
|
|
if tok != "|" {
|
|
return nil, p.errf("expected %q or %q, got %q", "}", "|", tok)
|
|
}
|
|
name, err := p.consumeToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defn.Members = append(defn.Members, name)
|
|
|
|
if tok, err := p.peekToken(); err == nil && tok == "(" {
|
|
p.consumePeeked()
|
|
key, err := p.consumeToken()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reprKeys = append(reprKeys, key)
|
|
if err := p.consumeRequired(")"); err != nil {
|
|
return defn, err
|
|
}
|
|
} else {
|
|
reprKeys = append(reprKeys, "")
|
|
}
|
|
}
|
|
|
|
reprName := "string" // default repr
|
|
if tok, err := p.peekToken(); err == nil && tok == "representation" {
|
|
p.consumePeeked()
|
|
name, err := p.consumeName()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reprName = name
|
|
}
|
|
switch reprName {
|
|
case "string":
|
|
repr := &dmt.EnumRepresentation_String{}
|
|
for i, key := range reprKeys {
|
|
if key == "" {
|
|
continue // no key; defaults to the name
|
|
}
|
|
if key[0] != '"' {
|
|
return nil, p.errf("enum string representation used with non-string key: %s", key)
|
|
}
|
|
unquoted, err := strconv.Unquote(key)
|
|
if err != nil {
|
|
return nil, p.forwardError(err)
|
|
}
|
|
mapAppend(repr, defn.Members[i], unquoted)
|
|
}
|
|
defn.Representation.EnumRepresentation_String = repr
|
|
case "int":
|
|
repr := &dmt.EnumRepresentation_Int{}
|
|
for i, key := range reprKeys {
|
|
if key[0] != '"' {
|
|
return nil, p.errf("enum int representation used with non-string key: %s", key)
|
|
}
|
|
unquoted, err := strconv.Unquote(key)
|
|
if err != nil {
|
|
return nil, p.forwardError(err)
|
|
}
|
|
parsed, err := strconv.Atoi(unquoted)
|
|
if err != nil {
|
|
return nil, p.forwardError(err)
|
|
}
|
|
mapAppend(repr, defn.Members[i], parsed)
|
|
}
|
|
defn.Representation.EnumRepresentation_Int = repr
|
|
default:
|
|
return nil, p.errf("unknown enum repr: %q", reprName)
|
|
}
|
|
return defn, nil
|
|
}
|