Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 190 additions & 96 deletions cmd/bencgen/README.md

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions cmd/bencgen/bcd/bcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ func (b *Bcd) buildMsgs(existingMsgs Msgs, force bool) Msgs {
newMsgs := Msgs{Msgs: make(map[string]Msg)}

for _, node := range b.Nodes {
if stmt, ok := node.(*parser.CtrStmt); ok {
if stmt, ok := node.(*parser.ContainerStmt); ok {
fields := make(map[uint16]parser.Field)
for _, field := range stmt.Fields {
fields[field.Id] = field
fields[field.ID] = field
}

if existingMsg, exists := existingMsgs.Msgs[stmt.Name]; !force && exists {
Expand All @@ -79,7 +79,7 @@ func (b *Bcd) buildMsgs(existingMsgs Msgs, force bool) Msgs {

newMsgs.Msgs[stmt.Name] = Msg{
Fields: fields,
ReservedIDs: stmt.ReservedIds,
ReservedIDs: stmt.ReservedIDs,
}
}
}
Expand All @@ -88,20 +88,20 @@ func (b *Bcd) buildMsgs(existingMsgs Msgs, force bool) Msgs {
return newMsgs
}

func (b *Bcd) checkForConflicts(existingMsg Msg, stmt *parser.CtrStmt, fields map[uint16]parser.Field) {
func (b *Bcd) checkForConflicts(existingMsg Msg, stmt *parser.ContainerStmt, fields map[uint16]parser.Field) {
for _, existingField := range existingMsg.Fields {
currentField, exists := fields[existingField.Id]
if !exists && !slices.Contains(stmt.ReservedIds, existingField.Id) {
b.handleError(fmt.Sprintf("Field \"%s\" (id \"%d\") on msg \"%s\" was removed, but \"%d\" is not marked as reserved.", existingField.Name, existingField.Id, stmt.Name, existingField.Id))
currentField, exists := fields[existingField.ID]
if !exists && !slices.Contains(stmt.ReservedIDs, existingField.ID) {
b.handleError(fmt.Sprintf("Field '%s' (id '%d') on msg '%s' was removed, but '%d' is not marked as reserved.", existingField.Name, existingField.ID, stmt.Name, existingField.ID))
}

if exists {
if slices.Contains(stmt.ReservedIds, currentField.Id) {
b.handleError(fmt.Sprintf("Field \"%s\" (id \"%d\") on msg \"%s\" may not be marked as reserved.", currentField.Name, currentField.Id, stmt.Name))
if slices.Contains(stmt.ReservedIDs, currentField.ID) {
b.handleError(fmt.Sprintf("Field '%s' (id '%d') on msg '%s' may not be marked as reserved.", currentField.Name, currentField.ID, stmt.Name))
}

if existingField.Id == currentField.Id && !utils.CompareTypes(existingField.Type, currentField.Type) {
b.handleError(fmt.Sprintf("Field \"%s\" (id \"%d\") on msg \"%s\" changed type from \"%s\" to \"%s\".", currentField.Name, currentField.Id, stmt.Name, utils.FormatType(existingField.Type), utils.FormatType(currentField.Type)))
if existingField.ID == currentField.ID && !utils.CompareTypes(existingField.Type, currentField.Type) {
b.handleError(fmt.Sprintf("Field '%s' (id '%d') on msg '%s' changed type from '%s' to '%s'.", currentField.Name, currentField.ID, stmt.Name, utils.FormatType(existingField.Type), utils.FormatType(currentField.Type)))
}
}
}
Expand Down
137 changes: 80 additions & 57 deletions cmd/bencgen/codegens/entry-gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Gen interface {
File() string
Lang() GenLang

GenHeader() string
GenDefine() string
GenEnum() string
GenStruct() string
GenReservedIds() string
Expand All @@ -39,14 +39,18 @@ type Gen interface {
GenMarshalPlain() string
GenUnmarshalPlain() string

HasHeader() bool
ProcessImport(stmt *parser.UseStmt, importDirs []string) ([]string, []string)

SetCtrDecls(ctrDecls []string)
SetEnumDecls(ctrDecls []string)
SetVarMap(map[string]string)

HasPackageDefined() bool

AddEnumDecls(enumDecls []string)
AddContainerDecls(containerDecls []string)

SetCtrStatement(stmt *parser.CtrStmt)
SetEnumStatement(stmt *parser.EnumStmt)
SetHeaderStatement(stmt *parser.HeaderStmt)
SetDefineStatement(stmt *parser.DefineStmt)
SetContainerStatement(stmt *parser.ContainerStmt)
}

func NewGen(lang GenLang, file string) Gen {
Expand All @@ -58,7 +62,7 @@ func NewGen(lang GenLang, file string) Gen {
}
}

func logErrorAndExit(g Gen, msg string) {
func LogErrorAndExit(g Gen, msg string) {
fmt.Printf("\n\033[1;31m[bencgen] Error:\033[0m\n"+
" \033[1;37mFile:\033[0m %s\n"+
" \033[1;37mMessage:\033[0m %s\n", g.File(), msg)
Expand All @@ -67,135 +71,154 @@ func logErrorAndExit(g Gen, msg string) {

var disallowedNames = []string{"b", "n", "id", "r"}

func Generate(g Gen, nodes []parser.Node) string {
ctrDecls := []string{}
func Generate(g Gen, nodes []parser.Node, importDirs []string) string {
enumDecls := []string{}
containerDecls := []string{}

varMap := make(map[string]string)

res := fmt.Sprintf("// Code generated by bencgen %s. DO NOT EDIT.\n// source: %s\n\n",
g.Lang().String(), g.File())

for _, node := range nodes {
switch stmt := node.(type) {
case *parser.CtrStmt:
validateCtrStmt(g, stmt, enumDecls, ctrDecls)
ctrDecls = append(ctrDecls, stmt.Name)
case *parser.UseStmt:
aEnumDecls, aContainerDecls := g.ProcessImport(stmt, importDirs)
enumDecls = append(enumDecls, aEnumDecls...)
containerDecls = append(containerDecls, aContainerDecls...)
}
}

for _, node := range nodes {
switch stmt := node.(type) {
case *parser.EnumStmt:
validateEnumStmt(g, stmt, enumDecls, ctrDecls)
validateEnumStmt(g, stmt, enumDecls, containerDecls)
enumDecls = append(enumDecls, stmt.Name)
case *parser.ContainerStmt:
validateCtrStmt(g, stmt, enumDecls, containerDecls)
containerDecls = append(containerDecls, stmt.Name)
case *parser.VarStmt:
varMap[stmt.Name] = stmt.Value
}
}

g.SetCtrDecls(ctrDecls)
g.SetEnumDecls(enumDecls)
g.AddEnumDecls(enumDecls)
g.AddContainerDecls(containerDecls)

g.SetVarMap(varMap)

for _, node := range nodes {
switch stmt := node.(type) {
case *parser.HeaderStmt:
if g.HasHeader() {
logErrorAndExit(g, "Multiple 'header' declarations.")
case *parser.DefineStmt:
if g.HasPackageDefined() {
LogErrorAndExit(g, "Multiple packages defined ( 'define ...' ).")
}

g.SetHeaderStatement(stmt)
res += g.GenHeader()
case *parser.CtrStmt:
if !g.HasHeader() {
logErrorAndExit(g, "A 'header' was not declared.")
}
validateContainerFields(g, stmt, ctrDecls, enumDecls)

g.SetCtrStatement(stmt)
res += generateCtr(g)
g.SetDefineStatement(stmt)
res += g.GenDefine()
case *parser.EnumStmt:
if !g.HasHeader() {
logErrorAndExit(g, "A 'header' was not declared.")
if !g.HasPackageDefined() {
LogErrorAndExit(g, "A package was not defined ( 'define ...' ).")
}
validateEnumFields(g, stmt)

g.SetEnumStatement(stmt)
res += g.GenEnum()
case *parser.ContainerStmt:
if !g.HasPackageDefined() {
LogErrorAndExit(g, "A package was not defined ( 'define ...' ).")
}
validateContainerFields(g, stmt, containerDecls, enumDecls)

g.SetContainerStatement(stmt)
res += generateContainer(g)
}
}

return res
}

func validateCtrStmt(g Gen, stmt *parser.CtrStmt, enumDecls []string, ctrDecls []string) {
func validateCtrStmt(g Gen, stmt *parser.ContainerStmt, enumDecls []string, containerDecls []string) {
if slices.Contains(enumDecls, stmt.Name) {
logErrorAndExit(g, fmt.Sprintf("A enum with the same name \"%s\" is already declared.", stmt.Name))
LogErrorAndExit(g, fmt.Sprintf("A enum with the same name '%s' is already declared.", stmt.Name))
}

if slices.Contains(ctrDecls, stmt.Name) {
logErrorAndExit(g, fmt.Sprintf("Multiple containers with the same name \"%s\".", stmt.Name))
if slices.Contains(containerDecls, stmt.Name) {
LogErrorAndExit(g, fmt.Sprintf("Multiple containers with the same name '%s'.", stmt.Name))
}

if len(stmt.Fields) == 0 {
logErrorAndExit(g, fmt.Sprintf("Empty container \"%s\".", stmt.Name))
LogErrorAndExit(g, fmt.Sprintf("Empty container '%s'.", stmt.Name))
}
}

func validateEnumStmt(g Gen, stmt *parser.EnumStmt, enumDecls []string, ctrDecls []string) {
func validateEnumStmt(g Gen, stmt *parser.EnumStmt, enumDecls []string, containerDecls []string) {
if slices.Contains(enumDecls, stmt.Name) {
logErrorAndExit(g, fmt.Sprintf("Multiple enums with the same name \"%s\".", stmt.Name))
LogErrorAndExit(g, fmt.Sprintf("Multiple enums with the same name '%s'.", stmt.Name))
}

if slices.Contains(ctrDecls, stmt.Name) {
logErrorAndExit(g, fmt.Sprintf("A container with the same name \"%s\" is already declared.", stmt.Name))
if slices.Contains(containerDecls, stmt.Name) {
LogErrorAndExit(g, fmt.Sprintf("A container with the same name '%s' is already declared.", stmt.Name))
}

if len(stmt.Fields) == 0 {
logErrorAndExit(g, fmt.Sprintf("Empty enum \"%s\".", stmt.Name))
if slices.Contains(disallowedNames, utils.ToLower(stmt.Name)) {
LogErrorAndExit(g, fmt.Sprintf("Disallowed enum name '%s'.", stmt.Name))
}

if len(stmt.Values) == 0 {
LogErrorAndExit(g, fmt.Sprintf("Empty enum '%s'.", stmt.Name))
}
}

func validateContainerFields(g Gen, stmt *parser.CtrStmt, ctrDecls []string, enumDecls []string) {
func validateContainerFields(g Gen, stmt *parser.ContainerStmt, enumDecls []string, containerDecls []string) {
var ids []uint16
var lastID uint16
var fieldNames []string

for _, field := range stmt.Fields {
_, enumNotFound := utils.FindUndeclaredContainersOrEnums(enumDecls, field.Type)
if ctrEnum, notFound := utils.FindUndeclaredContainersOrEnums(ctrDecls, field.Type); notFound && enumNotFound {
logErrorAndExit(g, fmt.Sprintf("Container/Enum \"%s\" not declared on \"%s\" (\"%s\").", ctrEnum, stmt.Name, field.Name))
if ctrEnum, notFound := utils.FindUndeclaredContainersOrEnums(containerDecls, field.Type); notFound && enumNotFound {
LogErrorAndExit(g, fmt.Sprintf("Container/Enum '%s' not declared on '%s' ('%s').", ctrEnum, stmt.Name, field.Name))
}

if field.Id == 0 {
logErrorAndExit(g, fmt.Sprintf("Field \"%s\" has an ID of \"0\" on \"%s\".", field.Name, stmt.Name))
if field.ID == 0 {
LogErrorAndExit(g, fmt.Sprintf("Field '%s' has an ID of '0' on '%s'.", field.Name, stmt.Name))
}

if slices.Contains(ids, field.Id) {
logErrorAndExit(g, fmt.Sprintf("Multiple fields with the same ID \"%d\" on \"%s\".", field.Id, stmt.Name))
if slices.Contains(ids, field.ID) {
LogErrorAndExit(g, fmt.Sprintf("Multiple fields with the same ID '%d' on '%s'.", field.ID, stmt.Name))
}

if lastID > field.Id {
logErrorAndExit(g, fmt.Sprintf("Fields must be ordered by their IDs in ascending order on \"%s\".", stmt.Name))
if lastID > field.ID {
LogErrorAndExit(g, fmt.Sprintf("Fields must be ordered by their IDs in ascending order on '%s'.", stmt.Name))
}

if slices.Contains(fieldNames, field.Name) {
logErrorAndExit(g, fmt.Sprintf("Multiple fields with the same name \"%s\" on \"%s\".", field.Name, stmt.Name))
LogErrorAndExit(g, fmt.Sprintf("Multiple fields with the same name '%s' on '%s'.", field.Name, stmt.Name))
}

if slices.Contains(disallowedNames, utils.ToLower(stmt.Name)) {
logErrorAndExit(g, fmt.Sprintf("Unallowed container name \"%s\".", stmt.Name))
LogErrorAndExit(g, fmt.Sprintf("Disallowed container name '%s'.", stmt.Name))
}

lastID = field.Id
ids = append(ids, field.Id)
lastID = field.ID
ids = append(ids, field.ID)
fieldNames = append(fieldNames, field.Name)
}
}

func validateEnumFields(g Gen, stmt *parser.EnumStmt) {
var fieldNames []string

for _, field := range stmt.Fields {
for _, field := range stmt.Values {
if slices.Contains(fieldNames, field) {
logErrorAndExit(g, fmt.Sprintf("Multiple fields with the same name \"%s\" on \"%s\".", field, stmt.Name))
LogErrorAndExit(g, fmt.Sprintf("Multiple values '%s' on '%s'.", field, stmt.Name))
}
fieldNames = append(fieldNames, field)
}
}

func generateCtr(g Gen) string {
func generateContainer(g Gen) string {
return g.GenStruct() +
g.GenReservedIds() +
g.GenSize() +
Expand Down
Loading