haven/nexgb/xgbgen/xml_fields.go

152 lines
4.5 KiB
Go

package main
/*
A series of fields should be taken as "structure contents", and *not*
just the single 'field' elements. Namely, 'fields' subsumes 'field'
elements.
More particularly, 'fields' corresponds to list, in order, of any of the
follow elements: pad, field, list, localfield, exprfield, valueparm
and switch.
Thus, the 'Field' type must contain the union of information corresponding
to all aforementioned fields.
This would ideally be a better job for interfaces, but I could not figure
out how to make them jive with Go's XML package. (And I don't really feel
up to type translation.)
*/
import (
"encoding/xml"
"fmt"
"log"
"strings"
)
type Fields []*Field
type Field struct {
XMLName xml.Name
// For 'pad' element
Bytes int `xml:"bytes,attr"`
// For 'field', 'list', 'localfield', 'exprfield' and 'switch' elements.
Name Name `xml:"name,attr"`
// For 'field', 'list', 'localfield', and 'exprfield' elements.
Type Type `xml:"type,attr"`
// For 'list', 'exprfield' and 'switch' elements.
Expr *Expression `xml:",any"`
// For 'valueparm' element.
ValueMaskType Type `xml:"value-mask-type,attr"`
ValueMaskName Name `xml:"value-mask-name,attr"`
ValueListName Name `xml:"value-list-name,attr"`
// For 'switch' element.
Bitcases Bitcases `xml:"bitcase"`
// I don't know which elements these are for. The documentation is vague.
// They also seem to be completely optional.
OptEnum Type `xml:"enum,attr"`
OptMask Type `xml:"mask,attr"`
OptAltEnum Type `xml:"altenum,attr"`
}
// String is for debugging purposes.
func (f *Field) String() string {
switch f.XMLName.Local {
case "pad":
return fmt.Sprintf("pad (%d bytes)", f.Bytes)
case "field":
return fmt.Sprintf("field (type = '%s', name = '%s')", f.Type, f.Name)
case "list":
return fmt.Sprintf("list (type = '%s', name = '%s', length = '%s')",
f.Type, f.Name, f.Expr)
case "localfield":
return fmt.Sprintf("localfield (type = '%s', name = '%s')",
f.Type, f.Name)
case "exprfield":
return fmt.Sprintf("exprfield (type = '%s', name = '%s', expr = '%s')",
f.Type, f.Name, f.Expr)
case "valueparam":
return fmt.Sprintf("valueparam (type = '%s', name = '%s', list = '%s')",
f.ValueMaskType, f.ValueMaskName, f.ValueListName)
case "switch":
bitcases := make([]string, len(f.Bitcases))
for i, bitcase := range f.Bitcases {
bitcases[i] = bitcase.StringPrefix("\t")
}
return fmt.Sprintf("switch (name = '%s', expr = '%s')\n\t%s",
f.Name, f.Expr, strings.Join(bitcases, "\n\t"))
default:
log.Panicf("Unrecognized field element: %s", f.XMLName.Local)
}
panic("unreachable")
}
type Bitcases []*Bitcase
// Bitcase represents a single expression followed by any number of fields.
// Namely, if the switch's expression (all bitcases are inside a switch),
// and'd with the bitcase's expression is equal to the bitcase expression,
// then the fields should be included in its parent structure.
// Note that since a bitcase is unique in that expressions and fields are
// siblings, we must exhaustively search for one of them. Essentially,
// it's the closest thing to a Union I can get to in Go without interfaces.
// Would an '<expression>' tag have been too much to ask? :-(
type Bitcase struct {
Fields Fields `xml:",any"`
// All the different expressions.
// When it comes time to choose one, use the 'Expr' method.
ExprOp *Expression `xml:"op"`
ExprUnOp *Expression `xml:"unop"`
ExprField *Expression `xml:"fieldref"`
ExprValue *Expression `xml:"value"`
ExprBit *Expression `xml:"bit"`
ExprEnum *Expression `xml:"enumref"`
ExprSum *Expression `xml:"sumof"`
ExprPop *Expression `xml:"popcount"`
}
// StringPrefix is for debugging purposes only.
// StringPrefix takes a string to prefix to every extra line for formatting.
func (b *Bitcase) StringPrefix(prefix string) string {
fields := make([]string, len(b.Fields))
for i, field := range b.Fields {
fields[i] = fmt.Sprintf("%s%s", prefix, field)
}
return fmt.Sprintf("%s\n\t%s%s", b.Expr(), prefix,
strings.Join(fields, "\n\t"))
}
// Expr chooses the only non-nil Expr* field from Bitcase.
// Panic if there is more than one non-nil expression.
func (b *Bitcase) Expr() *Expression {
choices := []*Expression{
b.ExprOp, b.ExprUnOp, b.ExprField, b.ExprValue,
b.ExprBit, b.ExprEnum, b.ExprSum, b.ExprPop,
}
var choice *Expression = nil
numNonNil := 0
for _, c := range choices {
if c != nil {
numNonNil++
choice = c
}
}
if choice == nil {
log.Panicf("No top level expression found in a bitcase.")
}
if numNonNil > 1 {
log.Panicf("More than one top-level expression was found in a bitcase.")
}
return choice
}