aboutsummaryrefslogtreecommitdiff
path: root/nexgb/xgbgen
diff options
context:
space:
mode:
authorAndrew Gallant (Ocelot) <Andrew.Gallant@tufts.edu>2012-04-28 23:25:57 -0400
committerAndrew Gallant (Ocelot) <Andrew.Gallant@tufts.edu>2012-04-28 23:25:57 -0400
commit52a21b415ad95b2c4649254447388cb329cee1a4 (patch)
tree0897f4033cc6251c7feb85fe9765159a3d0608eb /nexgb/xgbgen
downloadhaven-52a21b415ad95b2c4649254447388cb329cee1a4.tar.gz
haven-52a21b415ad95b2c4649254447388cb329cee1a4.tar.xz
haven-52a21b415ad95b2c4649254447388cb329cee1a4.zip
initial commit. not currently in a working state.
Diffstat (limited to 'nexgb/xgbgen')
-rw-r--r--nexgb/xgbgen/context.go89
-rw-r--r--nexgb/xgbgen/go.go255
-rw-r--r--nexgb/xgbgen/main.go64
-rw-r--r--nexgb/xgbgen/misc.go44
-rwxr-xr-xnexgb/xgbgen/xgbgenbin0 -> 2318165 bytes
-rw-r--r--nexgb/xgbgen/xml.go298
-rw-r--r--nexgb/xgbgen/xml_expression.go160
-rw-r--r--nexgb/xgbgen/xml_fields.go147
8 files changed, 1057 insertions, 0 deletions
diff --git a/nexgb/xgbgen/context.go b/nexgb/xgbgen/context.go
new file mode 100644
index 0000000..e5acb12
--- /dev/null
+++ b/nexgb/xgbgen/context.go
@@ -0,0 +1,89 @@
+package main
+
+import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
+ "log"
+ "strings"
+)
+
+type Context struct {
+ xml *XML
+ out *bytes.Buffer
+}
+
+func newContext() *Context {
+ return &Context{
+ xml: &XML{},
+ out: bytes.NewBuffer([]byte{}),
+ }
+}
+
+// Putln calls put and adds a new line to the end of 'format'.
+func (c *Context) Putln(format string, v ...interface{}) {
+ c.Put(format + "\n", v...)
+}
+
+// Put is a short alias to write to 'out'.
+func (c *Context) Put(format string, v ...interface{}) {
+ _, err := c.out.WriteString(fmt.Sprintf(format, v...))
+ if err != nil {
+ log.Fatalf("There was an error writing to context buffer: %s", err)
+ }
+}
+
+// TypePrefix searches the parsed XML for a type matching 'needle'.
+// It then returns the appropriate prefix to be used in source code.
+// Note that the core X protocol *is* a namespace, but does not have a prefix.
+// Also note that you should probably check the BaseTypeMap and TypeMap
+// before calling this function.
+func (c *Context) TypePrefix(needle Type) string {
+ // If this is xproto, quit. No prefixes needed.
+ if c.xml.Header == "xproto" {
+ return ""
+ }
+
+ // First check for the type in the current namespace.
+ if c.xml.HasType(needle) {
+ return strings.Title(c.xml.Header)
+ }
+
+ // Now check each of the imports...
+ for _, imp := range c.xml.Imports {
+ if imp.xml.Header != "xproto" && imp.xml.HasType(needle) {
+ return strings.Title(imp.xml.Header)
+ }
+ }
+
+ return ""
+}
+
+// Translate is the big daddy of them all. It takes in an XML byte slice
+// and writes Go code to the 'out' buffer.
+func (c *Context) Translate(xmlBytes []byte) {
+ err := xml.Unmarshal(xmlBytes, c.xml)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Parse all imports
+ c.xml.Imports.Eval()
+
+ // Make sure all top level enumerations have expressions
+ // (For when there are empty items.)
+ c.xml.Enums.Eval()
+
+ // It's Morphin' Time!
+ c.xml.Morph(c)
+
+ // for _, req := range c.xml.Requests {
+ // if req.Name != "CreateContext" && req.Name != "MakeCurrent" {
+ // continue
+ // }
+ // log.Println(req.Name)
+ // for _, field := range req.Fields {
+ // log.Println("\t", field.XMLName.Local, field.Type.Morph(c))
+ // }
+ // }
+}
diff --git a/nexgb/xgbgen/go.go b/nexgb/xgbgen/go.go
new file mode 100644
index 0000000..eb3f0fb
--- /dev/null
+++ b/nexgb/xgbgen/go.go
@@ -0,0 +1,255 @@
+package main
+/*
+ To the best of my ability, these are all of the Go specific formatting
+ functions. If I've designed xgbgen correctly, this should be only the
+ place that you change things to generate code for a new language.
+
+ This file is organized as follows:
+
+ * Imports and helper variables.
+ * Manual type and name override maps.
+ * Helper morphing functions.
+ * Morphing functions for each "unit".
+ * Morphing functions for collections of "units".
+*/
+
+import (
+ "strings"
+)
+
+/******************************************************************************/
+// Manual type and name overrides.
+/******************************************************************************/
+
+// BaseTypeMap is a map from X base types to Go types.
+// X base types should correspond to the smallest set of X types
+// that can be used to rewrite ALL X types in terms of Go types.
+// That is, if you remove any of the following types, at least one
+// XML protocol description will produce an invalid Go program.
+// The types on the left *never* show themselves in the source.
+var BaseTypeMap = map[string]string{
+ "CARD8": "byte",
+ "CARD16": "uint16",
+ "CARD32": "uint32",
+ "INT8": "int8",
+ "INT16": "int16",
+ "INT32": "int32",
+ "BYTE": "byte",
+ "BOOL": "bool",
+ "float": "float64",
+ "double": "float64",
+}
+
+// TypeMap is a map from types in the XML to type names that is used
+// in the functions that follow. Basically, every occurrence of the key
+// type is replaced with the value type.
+var TypeMap = map[string]string{
+ "VISUALTYPE": "VisualInfo",
+ "DEPTH": "DepthInfo",
+ "SCREEN": "ScreenInfo",
+ "Setup": "SetupInfo",
+}
+
+// NameMap is the same as TypeMap, but for names.
+var NameMap = map[string]string{ }
+
+/******************************************************************************/
+// Helper functions that aide in morphing repetive constructs.
+// i.e., "structure contents", expressions, type and identifier names, etc.
+/******************************************************************************/
+
+// Morph changes every TYPE (not names) into something suitable
+// for your language.
+func (typ Type) Morph(c *Context) string {
+ t := string(typ)
+
+ // If this is a base type, then write the raw Go type.
+ if newt, ok := BaseTypeMap[t]; ok {
+ return newt
+ }
+
+ // If it's in the type map, use that translation.
+ if newt, ok := TypeMap[t]; ok {
+ return newt
+ }
+
+ // If it's a resource type, just use 'Id'.
+ if c.xml.IsResource(typ) {
+ return "Id"
+ }
+
+ // If there's a namespace to this type, just use it and be done.
+ if colon := strings.Index(t, ":"); colon > -1 {
+ namespace := t[:colon]
+ rest := t[colon+1:]
+ return splitAndTitle(namespace) + splitAndTitle(rest)
+ }
+
+ // Since there is no namespace, we need to look for a namespace
+ // in the current context.
+ return c.TypePrefix(typ) + splitAndTitle(t)
+}
+
+// Morph changes every identifier (NOT type) into something suitable
+// for your language.
+func (name Name) Morph(c *Context) string {
+ n := string(name)
+
+ // If it's in the name map, use that translation.
+ if newn, ok := NameMap[n]; ok {
+ return newn
+ }
+
+ return splitAndTitle(n)
+}
+
+/******************************************************************************/
+// Per element morphing.
+// Below are functions that morph a single unit.
+/******************************************************************************/
+
+// Import morphing.
+func (imp *Import) Morph(c *Context) {
+ c.Putln("// import \"%s\"", imp.Name)
+}
+
+// Enum morphing.
+func (enum *Enum) Morph(c *Context) {
+ c.Putln("const (")
+ for _, item := range enum.Items {
+ c.Putln("%s%s = %d", enum.Name.Morph(c), item.Name.Morph(c),
+ item.Expr.Eval())
+ }
+ c.Putln(")\n")
+}
+
+// Xid morphing.
+func (xid *Xid) Morph(c *Context) {
+ // Don't emit anything for xid types for now.
+ // We're going to force them all to simply be 'Id'
+ // to avoid excessive type converting.
+ // c.Putln("type %s Id", xid.Name.Morph(c))
+}
+
+// TypeDef morphing.
+func (typedef *TypeDef) Morph(c *Context) {
+ c.Putln("type %s %s", typedef.Old.Morph(c), typedef.New.Morph(c))
+}
+
+// Struct morphing.
+func (strct *Struct) Morph(c *Context) {
+}
+
+// Union morphing.
+func (union *Union) Morph(c *Context) {
+}
+
+// Request morphing.
+func (request *Request) Morph(c *Context) {
+}
+
+// Event morphing.
+func (ev *Event) Morph(c *Context) {
+}
+
+// EventCopy morphing.
+func (evcopy *EventCopy) Morph(c *Context) {
+}
+
+// Error morphing.
+func (err *Error) Morph(c *Context) {
+}
+
+// ErrorCopy morphing.
+func (errcopy *ErrorCopy) Morph(c *Context) {
+}
+
+/******************************************************************************/
+// Collection morphing.
+// Below are functions that morph a collections of units.
+// Most of these can probably remain unchanged, but they are useful if you
+// need to group all of some "unit" in a single block or something.
+/******************************************************************************/
+func (imports Imports) Morph(c *Context) {
+ if len(imports) == 0 {
+ return
+ }
+
+ c.Putln("// Imports are not required for XGB since everything is in")
+ c.Putln("// a single package. Still these may be useful for ")
+ c.Putln("// reference purposes.")
+ for _, imp := range imports {
+ imp.Morph(c)
+ }
+}
+
+func (enums Enums) Morph(c *Context) {
+ c.Putln("// Enums\n")
+ for _, enum := range enums {
+ enum.Morph(c)
+ }
+}
+
+func (xids Xids) Morph(c *Context) {
+ c.Putln("// Xids\n")
+ for _, xid := range xids {
+ xid.Morph(c)
+ }
+}
+
+func (typedefs TypeDefs) Morph(c *Context) {
+ c.Putln("// TypeDefs\n")
+ for _, typedef := range typedefs {
+ typedef.Morph(c)
+ }
+}
+
+func (strct Structs) Morph(c *Context) {
+ c.Putln("// Structs\n")
+ for _, typedef := range strct {
+ typedef.Morph(c)
+ }
+}
+
+func (union Unions) Morph(c *Context) {
+ c.Putln("// Unions\n")
+ for _, typedef := range union {
+ typedef.Morph(c)
+ }
+}
+
+func (request Requests) Morph(c *Context) {
+ c.Putln("// Requests\n")
+ for _, typedef := range request {
+ typedef.Morph(c)
+ }
+}
+
+func (event Events) Morph(c *Context) {
+ c.Putln("// Events\n")
+ for _, typedef := range event {
+ typedef.Morph(c)
+ }
+}
+
+func (evcopy EventCopies) Morph(c *Context) {
+ c.Putln("// Event Copies\n")
+ for _, typedef := range evcopy {
+ typedef.Morph(c)
+ }
+}
+
+func (err Errors) Morph(c *Context) {
+ c.Putln("// Errors\n")
+ for _, typedef := range err {
+ typedef.Morph(c)
+ }
+}
+
+func (errcopy ErrorCopies) Morph(c *Context) {
+ c.Putln("// Error copies\n")
+ for _, typedef := range errcopy {
+ typedef.Morph(c)
+ }
+}
+
diff --git a/nexgb/xgbgen/main.go b/nexgb/xgbgen/main.go
new file mode 100644
index 0000000..69579a4
--- /dev/null
+++ b/nexgb/xgbgen/main.go
@@ -0,0 +1,64 @@
+package main
+
+import (
+ "flag"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+var (
+ protoPath = flag.String("proto-path",
+ "/usr/share/xcb", "path to directory of X protocol XML files")
+ gofmt = flag.Bool("gofmt", true,
+ "When disabled, gofmt will not be run before outputting Go code")
+)
+
+func usage() {
+ basename := os.Args[0]
+ if lastSlash := strings.LastIndex(basename, "/"); lastSlash > -1 {
+ basename = basename[lastSlash+1:]
+ }
+ log.Printf("Usage: %s [flags] xml-file", basename)
+ flag.PrintDefaults()
+ os.Exit(1)
+}
+
+func init() {
+ log.SetFlags(0)
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+
+ if flag.NArg() != 1 {
+ log.Printf("A single XML protocol file can be processed at once.")
+ flag.Usage()
+ }
+
+ // Read the single XML file into []byte
+ xmlBytes, err := ioutil.ReadFile(flag.Arg(0))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Initialize the buffer, parse it, and filter it through gofmt.
+ c := newContext()
+ c.Translate(xmlBytes)
+
+ if !*gofmt {
+ c.out.WriteTo(os.Stdout)
+ } else {
+ cmdGofmt := exec.Command("gofmt")
+ cmdGofmt.Stdin = c.out
+ cmdGofmt.Stdout = os.Stdout
+ err = cmdGofmt.Run()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
diff --git a/nexgb/xgbgen/misc.go b/nexgb/xgbgen/misc.go
new file mode 100644
index 0000000..9adcf5d
--- /dev/null
+++ b/nexgb/xgbgen/misc.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "regexp"
+ "strings"
+)
+
+// AllCaps is a regex to test if a string identifier is made of
+// all upper case letters.
+var AllCaps = regexp.MustCompile("^[A-Z0-9]+$")
+
+// popCount counts number of bits 'set' in mask.
+func popCount(mask uint) uint {
+ m := uint32(mask)
+ n := uint(0)
+ for i := uint32(0); i < 32; i++ {
+ if m&(1<<i) != 0 {
+ n++
+ }
+ }
+ return n
+}
+
+// splitAndTitle takes a string, splits it by underscores, capitalizes the
+// first letter of each chunk, and smushes'em back together.
+func splitAndTitle(s string) string {
+ // If the string is all caps, lower it and capitalize first letter.
+ if AllCaps.MatchString(s) {
+ return strings.Title(strings.ToLower(s))
+ }
+
+ // If the string has no underscores, leave it be.
+ if i := strings.Index(s, "_"); i == -1 {
+ return s
+ }
+
+ // Now split the name at underscores, capitalize the first
+ // letter of each chunk, and smush'em back together.
+ chunks := strings.Split(s, "_")
+ for i, chunk := range chunks {
+ chunks[i] = strings.Title(strings.ToLower(chunk))
+ }
+ return strings.Join(chunks, "")
+}
diff --git a/nexgb/xgbgen/xgbgen b/nexgb/xgbgen/xgbgen
new file mode 100755
index 0000000..ef33abc
--- /dev/null
+++ b/nexgb/xgbgen/xgbgen
Binary files differ
diff --git a/nexgb/xgbgen/xml.go b/nexgb/xgbgen/xml.go
new file mode 100644
index 0000000..0f632b4
--- /dev/null
+++ b/nexgb/xgbgen/xml.go
@@ -0,0 +1,298 @@
+package main
+
+import (
+ "encoding/xml"
+ "io/ioutil"
+ "log"
+ "time"
+)
+
+type XML struct {
+ // Root 'xcb' element properties.
+ XMLName xml.Name `xml:"xcb"`
+ Header string `xml:"header,attr"`
+ ExtensionXName string `xml:"extension-xname,attr"`
+ ExtensionName string `xml:"extension-name,attr"`
+ MajorVersion string `xml:"major-version,attr"`
+ MinorVersion string `xml:"minor-version,attr"`
+
+ // Types for all top-level elements.
+ // First are the simple ones.
+ Imports Imports `xml:"import"`
+ Enums Enums `xml:"enum"`
+ Xids Xids `xml:"xidtype"`
+ XidUnions Xids `xml:"xidunion"`
+ TypeDefs TypeDefs `xml:"typedef"`
+ EventCopies EventCopies `xml:"eventcopy"`
+ ErrorCopies ErrorCopies `xml:"errorcopy"`
+
+ // Here are the complex ones, i.e., anything with "structure contents"
+ Structs Structs `xml:"struct"`
+ Unions Unions `xml:"union"`
+ Requests Requests `xml:"request"`
+ Events Events `xml:"event"`
+ Errors Errors `xml:"error"`
+}
+
+// Morph cascades down all of the XML and calls each type's corresponding
+// Morph function with itself as an argument (the context).
+func (x *XML) Morph(c *Context) {
+ // Start the header...
+ c.Putln("package xgb")
+ c.Putln("/*")
+ c.Putln("\tX protocol API for '%s.xml'.", c.xml.Header)
+ c.Putln("\tThis file is automatically generated. Edit at your own peril!")
+ c.Putln("\tGenerated on %s",
+ time.Now().Format("Jan 2, 2006 at 3:04:05pm MST"))
+ c.Putln("*/")
+ c.Putln("")
+
+ x.Imports.Morph(c)
+ c.Putln("")
+
+ x.Enums.Morph(c)
+ c.Putln("")
+
+ x.Xids.Morph(c)
+ c.Putln("")
+
+ x.XidUnions.Morph(c)
+ c.Putln("")
+
+ x.TypeDefs.Morph(c)
+ c.Putln("")
+
+ x.Structs.Morph(c)
+ c.Putln("")
+
+ x.Unions.Morph(c)
+ c.Putln("")
+
+ x.Requests.Morph(c)
+ c.Putln("")
+
+ x.Events.Morph(c)
+ c.Putln("")
+
+ x.Errors.Morph(c)
+ c.Putln("")
+
+ x.EventCopies.Morph(c)
+ c.Putln("")
+
+ x.ErrorCopies.Morph(c)
+ c.Putln("")
+}
+
+// IsResource returns true if the 'needle' type is a resource type.
+// i.e., an "xid"
+func (x *XML) IsResource(needle Type) bool {
+ for _, xid := range x.Xids {
+ if needle == xid.Name {
+ return true
+ }
+ }
+ for _, xidunion := range x.XidUnions {
+ if needle == xidunion.Name {
+ return true
+ }
+ }
+ for _, imp := range x.Imports {
+ if imp.xml.IsResource(needle) {
+ return true
+ }
+ }
+ return false
+}
+
+// HasType returns true if the 'needle' type can be found in the protocol
+// description represented by 'x'.
+func (x *XML) HasType(needle Type) bool {
+ for _, enum := range x.Enums {
+ if needle == enum.Name {
+ return true
+ }
+ }
+ for _, xid := range x.Xids {
+ if needle == xid.Name {
+ return true
+ }
+ }
+ for _, xidunion := range x.XidUnions {
+ if needle == xidunion.Name {
+ return true
+ }
+ }
+ for _, typedef := range x.TypeDefs {
+ if needle == typedef.New {
+ return true
+ }
+ }
+ for _, evcopy := range x.EventCopies {
+ if needle == evcopy.Name {
+ return true
+ }
+ }
+ for _, errcopy := range x.ErrorCopies {
+ if needle == errcopy.Name {
+ return true
+ }
+ }
+ for _, strct := range x.Structs {
+ if needle == strct.Name {
+ return true
+ }
+ }
+ for _, union := range x.Unions {
+ if needle == union.Name {
+ return true
+ }
+ }
+ for _, ev := range x.Events {
+ if needle == ev.Name {
+ return true
+ }
+ }
+ for _, err := range x.Errors {
+ if needle == err.Name {
+ return true
+ }
+ }
+
+ return false
+}
+
+type Name string
+
+type Type string
+
+type Imports []*Import
+
+func (imports Imports) Eval() {
+ for _, imp := range imports {
+ xmlBytes, err := ioutil.ReadFile(*protoPath + "/" + imp.Name + ".xml")
+ if err != nil {
+ log.Fatalf("Could not read X protocol description for import " +
+ "'%s' because: %s", imp.Name, err)
+ }
+
+ imp.xml = &XML{}
+ err = xml.Unmarshal(xmlBytes, imp.xml)
+ if err != nil {
+ log.Fatal("Could not parse X protocol description for import " +
+ "'%s' because: %s", imp.Name, err)
+ }
+ }
+}
+
+type Import struct {
+ Name string `xml:",chardata"`
+ xml *XML `xml:"-"`
+}
+
+type Enums []Enum
+
+// Eval on the list of all enum types goes through and forces every enum
+// item to have a valid expression.
+// This is necessary because when an item is empty, it is defined to have
+// the value of "one more than that of the previous item, or 0 for the first
+// item".
+func (enums Enums) Eval() {
+ for _, enum := range enums {
+ nextValue := uint(0)
+ for _, item := range enum.Items {
+ if item.Expr == nil {
+ item.Expr = newValueExpression(nextValue)
+ nextValue++
+ } else {
+ nextValue = item.Expr.Eval() + 1
+ }
+ }
+ }
+}
+
+type Enum struct {
+ Name Type `xml:"name,attr"`
+ Items []*EnumItem `xml:"item"`
+}
+
+type EnumItem struct {
+ Name Name `xml:"name,attr"`
+ Expr *Expression `xml:",any"`
+}
+
+type Xids []*Xid
+
+type Xid struct {
+ XMLName xml.Name
+ Name Type `xml:"name,attr"`
+}
+
+type TypeDefs []*TypeDef
+
+type TypeDef struct {
+ Old Type `xml:"oldname,attr"`
+ New Type `xml:"newname,attr"`
+}
+
+type EventCopies []*EventCopy
+
+type EventCopy struct {
+ Name Type `xml:"name,attr"`
+ Number string `xml:"number,attr"`
+ Ref Type `xml:"ref,attr"`
+}
+
+type ErrorCopies []*ErrorCopy
+
+type ErrorCopy struct {
+ Name Type `xml:"name,attr"`
+ Number string `xml:"number,attr"`
+ Ref Type `xml:"ref,attr"`
+}
+
+type Structs []*Struct
+
+type Struct struct {
+ Name Type `xml:"name,attr"`
+ Fields []*Field `xml:",any"`
+}
+
+type Unions []*Union
+
+type Union struct {
+ Name Type `xml:"name,attr"`
+ Fields []*Field `xml:",any"`
+}
+
+type Requests []*Request
+
+type Request struct {
+ Name Type `xml:"name,attr"`
+ Opcode int `xml:"opcode,attr"`
+ Combine bool `xml:"combine-adjacent,attr"`
+ Fields []*Field `xml:",any"`
+ Reply *Reply `xml:"reply"`
+}
+
+type Reply struct {
+ Fields []*Field `xml:",any"`
+}
+
+type Events []*Event
+
+type Event struct {
+ Name Type `xml:"name,attr"`
+ Number int `xml:"number,attr"`
+ NoSequence bool `xml:"no-sequence-number,true"`
+ Fields []*Field `xml:",any"`
+}
+
+type Errors []*Error
+
+type Error struct {
+ Name Type `xml:"name,attr"`
+ Number int `xml:"number,attr"`
+ Fields []*Field `xml:",any"`
+}
+
diff --git a/nexgb/xgbgen/xml_expression.go b/nexgb/xgbgen/xml_expression.go
new file mode 100644
index 0000000..dd32512
--- /dev/null
+++ b/nexgb/xgbgen/xml_expression.go
@@ -0,0 +1,160 @@
+package main
+
+import (
+ "encoding/xml"
+ "fmt"
+ "log"
+ "strconv"
+)
+
+type Expression struct {
+ XMLName xml.Name
+
+ Exprs []*Expression `xml:",any"`
+
+ Data string `xml:",chardata"`
+ Op string `xml:"op,attr"`
+ Ref string `xml:"ref,attr"`
+}
+
+func newValueExpression(v uint) *Expression {
+ return &Expression{
+ XMLName: xml.Name{Local: "value"},
+ Data: fmt.Sprintf("%d", v),
+ }
+}
+
+// String is for debugging. For actual use, please use 'Morph'.
+func (e *Expression) String() string {
+ switch e.XMLName.Local {
+ case "op":
+ return fmt.Sprintf("(%s %s %s)", e.Exprs[0], e.Op, e.Exprs[1])
+ case "unop":
+ return fmt.Sprintf("(%s (%s))", e.Op, e.Exprs[0])
+ case "popcount":
+ return fmt.Sprintf("popcount(%s)", e.Exprs[0])
+ case "fieldref":
+ fallthrough
+ case "value":
+ return fmt.Sprintf("%s", e.Data)
+ case "bit":
+ return fmt.Sprintf("(1 << %s)", e.Data)
+ case "enumref":
+ return fmt.Sprintf("%s%s", e.Ref, e.Data)
+ case "sumof":
+ return fmt.Sprintf("sum(%s)", e.Ref)
+ default:
+ log.Panicf("Unrecognized expression element: %s", e.XMLName.Local)
+ }
+
+ panic("unreachable")
+}
+
+// Eval is used to *attempt* to compute a concrete value for a particular
+// expression. This is used in the initial setup to instantiate values for
+// empty items in enums.
+// We can't compute a concrete value for expressions that rely on a context,
+// i.e., some field value.
+func (e *Expression) Eval() uint {
+ switch e.XMLName.Local {
+ case "op":
+ if len(e.Exprs) != 2 {
+ log.Panicf("'op' found %d expressions; expected 2.", len(e.Exprs))
+ }
+ return e.BinaryOp(e.Exprs[0], e.Exprs[1]).Eval()
+ case "unop":
+ if len(e.Exprs) != 1 {
+ log.Panicf("'unop' found %d expressions; expected 1.", len(e.Exprs))
+ }
+ return e.UnaryOp(e.Exprs[0]).Eval()
+ case "popcount":
+ if len(e.Exprs) != 1 {
+ log.Panicf("'popcount' found %d expressions; expected 1.",
+ len(e.Exprs))
+ }
+ return popCount(e.Exprs[0].Eval())
+ case "value":
+ val, err := strconv.Atoi(e.Data)
+ if err != nil {
+ log.Panicf("Could not convert '%s' in 'value' expression to int.",
+ e.Data)
+ }
+ return uint(val)
+ case "bit":
+ bit, err := strconv.Atoi(e.Data)
+ if err != nil {
+ log.Panicf("Could not convert '%s' in 'bit' expression to int.",
+ e.Data)
+ }
+ if bit < 0 || bit > 31 {
+ log.Panicf("A 'bit' literal must be in the range [0, 31], but " +
+ " is %d", bit)
+ }
+ return 1 << uint(bit)
+ case "fieldref":
+ log.Panicf("Cannot compute concrete value of 'fieldref' in " +
+ "expression '%s'.", e)
+ case "enumref":
+ log.Panicf("Cannot compute concrete value of 'enumref' in " +
+ "expression '%s'.", e)
+ case "sumof":
+ log.Panicf("Cannot compute concrete value of 'sumof' in " +
+ "expression '%s'.", e)
+ }
+
+ log.Panicf("Unrecognized tag '%s' in expression context. Expected one of " +
+ "op, fieldref, value, bit, enumref, unop, sumof or popcount.",
+ e.XMLName.Local)
+ panic("unreachable")
+}
+
+func (e *Expression) BinaryOp(operand1, operand2 *Expression) *Expression {
+ if e.XMLName.Local != "op" {
+ log.Panicf("Cannot perform binary operation on non-op expression: %s",
+ e.XMLName.Local)
+ }
+ if len(e.Op) == 0 {
+ log.Panicf("Cannot perform binary operation without operator for: %s",
+ e.XMLName.Local)
+ }
+
+ wrap := newValueExpression
+ switch e.Op {
+ case "+":
+ return wrap(operand1.Eval() + operand2.Eval())
+ case "-":
+ return wrap(operand1.Eval() + operand2.Eval())
+ case "*":
+ return wrap(operand1.Eval() * operand2.Eval())
+ case "/":
+ return wrap(operand1.Eval() / operand2.Eval())
+ case "&amp;":
+ return wrap(operand1.Eval() & operand2.Eval())
+ case "&lt;&lt;":
+ return wrap(operand1.Eval() << operand2.Eval())
+ }
+
+ log.Panicf("Invalid binary operator '%s' for '%s' expression.",
+ e.Op, e.XMLName.Local)
+ panic("unreachable")
+}
+
+func (e *Expression) UnaryOp(operand *Expression) *Expression {
+ if e.XMLName.Local != "unop" {
+ log.Panicf("Cannot perform unary operation on non-unop expression: %s",
+ e.XMLName.Local)
+ }
+ if len(e.Op) == 0 {
+ log.Panicf("Cannot perform unary operation without operator for: %s",
+ e.XMLName.Local)
+ }
+
+ switch e.Op {
+ case "~":
+ return newValueExpression(^operand.Eval())
+ }
+
+ log.Panicf("Invalid unary operator '%s' for '%s' expression.",
+ e.Op, e.XMLName.Local)
+ panic("unreachable")
+}
diff --git a/nexgb/xgbgen/xml_fields.go b/nexgb/xgbgen/xml_fields.go
new file mode 100644
index 0000000..18be6e3
--- /dev/null
+++ b/nexgb/xgbgen/xml_fields.go
@@ -0,0 +1,147 @@
+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 Field struct {
+ XMLName xml.Name
+
+ // For 'pad' element
+ Bytes int `xml:"bytes,attr"`
+
+ // For 'field', 'list', 'localfield', 'exprfield' and 'switch' elements.
+ Name string `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 string `xml:"value-mask-name,attr"`
+ ValueListName string `xml:"value-list-name,attr"`
+
+ // For 'switch' element.
+ Bitcases []*Bitcase `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")
+}
+
+// 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 []*Field `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
+}