aboutsummaryrefslogtreecommitdiff
path: root/nexgb
diff options
context:
space:
mode:
authorAndrew Gallant (Ocelot) <Andrew.Gallant@tufts.edu>2012-05-06 02:21:31 -0400
committerAndrew Gallant (Ocelot) <Andrew.Gallant@tufts.edu>2012-05-06 02:21:31 -0400
commit18b2d420b092d71313f0c05210c3038ff32483e7 (patch)
treee934f6bddd11863e82d591fb2a168d60be382f9d /nexgb
parent99bc76de54729df5494faa944d4da96a8885d51e (diff)
downloadhaven-18b2d420b092d71313f0c05210c3038ff32483e7.tar.gz
haven-18b2d420b092d71313f0c05210c3038ff32483e7.tar.xz
haven-18b2d420b092d71313f0c05210c3038ff32483e7.zip
added documentation and did some slight restructuring. it's party time.
Diffstat (limited to 'nexgb')
-rw-r--r--nexgb/xgbgen/context.go2
-rw-r--r--nexgb/xgbgen/doc.go74
-rw-r--r--nexgb/xgbgen/expression.go41
-rw-r--r--nexgb/xgbgen/field.go60
-rw-r--r--nexgb/xgbgen/misc.go4
-rw-r--r--nexgb/xgbgen/protocol.go41
-rw-r--r--nexgb/xgbgen/request_reply.go (renamed from nexgb/xgbgen/representation.go)62
-rw-r--r--nexgb/xgbgen/size.go9
-rw-r--r--nexgb/xgbgen/xml.go64
-rw-r--r--nexgb/xgbgen/xml_expression.go160
-rw-r--r--nexgb/xgbgen/xml_fields.go71
11 files changed, 284 insertions, 304 deletions
diff --git a/nexgb/xgbgen/context.go b/nexgb/xgbgen/context.go
index 3e484f3..d433531 100644
--- a/nexgb/xgbgen/context.go
+++ b/nexgb/xgbgen/context.go
@@ -8,6 +8,8 @@ import (
"time"
)
+// Context represents the protocol we're converting to Go, and a writer
+// buffer to write the Go source to.
type Context struct {
protocol *Protocol
out *bytes.Buffer
diff --git a/nexgb/xgbgen/doc.go b/nexgb/xgbgen/doc.go
new file mode 100644
index 0000000..4ba6145
--- /dev/null
+++ b/nexgb/xgbgen/doc.go
@@ -0,0 +1,74 @@
+/*
+xgbgen constructs Go source files from xproto XML description files. xgbgen
+accomplishes the same task as the Python code generator for XCB and xpyb.
+
+Usage:
+ xgbgen [flags] some-protocol.xml
+
+The flags are:
+ --proto-path path
+ The path to a directory containing xproto XML description files.
+ This is only necessary when 'some-protocol.xml' imports other
+ protocol files.
+ --gofmt=true
+ When false, the outputted Go code will not be gofmt'd. And it won't
+ be very pretty at all. This is typically useful if there are syntax
+ errors that need to be debugged in code generation. gofmt will hiccup;
+ this will allow you to see the raw code.
+
+How it works
+
+xgbgen works by parsing the input XML file using Go's encoding/xml package.
+The majority of this work is done in xml.go and xml_fields.go, where the
+appropriate types are declared.
+
+Due to the nature of the XML in the protocol description files, the types
+required to parse the XML are not well suited to reasoning about code
+generation. Because of this, all data parsed in the XML types is translated
+into more reasonable types. This translation is done in translation.go,
+and is mainly grunt work. (The only interesting tidbits are the translation
+of XML names to Go source names, and connecting fields with their
+appropriate types.)
+
+The organization of these types is greatly
+inspired by the description of the XML found here:
+http://cgit.freedesktop.org/xcb/proto/tree/doc/xml-xcb.txt
+
+These types come with a lot of supporting methods to make their use in
+code generation easier. They can be found in expression.go, field.go,
+protocol.go, request_reply.go and type.go. Of particular interest are
+expression evaluation and size calculation (in bytes).
+
+These types also come with supporting methods that convert their
+representation into Go source code. I've quartered such methods in
+go.go, go_error.go, go_event.go, go_list.go, go_request_reply.go,
+go_single_field.go, go_struct.go and go_union.go. The idea is to keep
+as much of the Go specific code generation in one area as possible. Namely,
+while not *all* Go related code is found in the 'go*.go' files, *most*
+of it is. (If there's any interest in using xgbgen for other languages,
+I'd be happy to try and make xgbgen a little more friendly in this regard.
+I did, however, design xgbgen with this in mind, so it shouldn't involve
+anything as serious as a re-design.)
+
+Why
+
+I wrote xgbgen because I found the existing code generator that was written in
+Python to be unwieldy. In particular, static and strong typing greatly helped
+me reason better about the code generation task.
+
+What does not work
+
+The core X protocol should be completely working. As far as I know, most
+extensions should work too, although I've only tested (and not much) the
+Xinerama and RandR extensions.
+
+XKB does not work. I don't have any real plans of working on this unless there
+is demand and I have some test cases to work with. (i.e., even if I could get
+something generated for XKB, I don't have the inclination to understand it
+enough to verify that it works.) XKB poses several extremely difficult
+problems that XCB also has trouble with. More info on that can be found at
+http://cgit.freedesktop.org/xcb/libxcb/tree/doc/xkb_issues and
+http://cgit.freedesktop.org/xcb/libxcb/tree/doc/xkb_internals.
+
+*/
+package main
diff --git a/nexgb/xgbgen/expression.go b/nexgb/xgbgen/expression.go
index 2350979..721ebfd 100644
--- a/nexgb/xgbgen/expression.go
+++ b/nexgb/xgbgen/expression.go
@@ -5,11 +5,32 @@ import (
"log"
)
+// Expression represents all the different forms of expressions possible in
+// side an XML protocol description file. It's also received a few custom
+// addendums to make applying special functions (like padding) easier.
type Expression interface {
+ // Concrete determines whether this particular expression can be computed
+ // to some constant value inside xgbgen. (The alternative is that the
+ // expression can only be computed with values at run time of the
+ // generated code.)
Concrete() bool
+
+ // Eval evaluates a concrete expression. It is an error to call Eval
+ // on any expression that is not concrete (or contains any sub-expression
+ // that is not concrete).
Eval() uint
+
+ // Reduce attempts to evaluate any concrete sub-expressions.
+ // i.e., (1 + 2 * (5 + 1 + someSizeOfStruct) reduces to
+ // (3 * (6 + someSizeOfStruct)).
+ // 'prefix' is used preprended to any field reference name.
Reduce(prefix string) string
+
+ // String is an alias for Reduce("")
String() string
+
+ // Initialize makes sure all names in this expression and any subexpressions
+ // have been translated to Go source names.
Initialize(p *Protocol)
}
@@ -41,12 +62,17 @@ func (e *Function) Initialize(p *Protocol) {
e.Expr.Initialize(p)
}
+// BinaryOp is an expression that performs some operation (defined in the XML
+// file) with Expr1 and Expr2 as operands.
type BinaryOp struct {
Op string
Expr1 Expression
Expr2 Expression
}
+// newBinaryOp constructs a new binary expression when both expr1 and expr2
+// are not nil. If one or both are nil, then the non-nil expression is
+// returned unchanged or nil is returned.
func newBinaryOp(op string, expr1, expr2 Expression) Expression {
switch {
case expr1 != nil && expr2 != nil:
@@ -124,6 +150,8 @@ func (e *BinaryOp) Initialize(p *Protocol) {
e.Expr2.Initialize(p)
}
+// UnaryOp is the same as BinaryOp, except it's a unary operator with only
+// one sub-expression.
type UnaryOp struct {
Op string
Expr Expression
@@ -158,6 +186,8 @@ func (e *UnaryOp) Initialize(p *Protocol) {
e.Expr.Initialize(p)
}
+// Padding represents the application of the 'pad' function to some
+// sub-expression.
type Padding struct {
Expr Expression
}
@@ -185,6 +215,8 @@ func (e *Padding) Initialize(p *Protocol) {
e.Expr.Initialize(p)
}
+// PopCount represents the application of the 'PopCount' function to
+// some sub-expression.
type PopCount struct {
Expr Expression
}
@@ -212,6 +244,7 @@ func (e *PopCount) Initialize(p *Protocol) {
e.Expr.Initialize(p)
}
+// Value represents some constant integer.
type Value struct {
v uint
}
@@ -234,6 +267,7 @@ func (e *Value) String() string {
func (e *Value) Initialize(p *Protocol) {}
+// Bit represents some bit whose value is computed by '1 << bit'.
type Bit struct {
b uint
}
@@ -256,6 +290,8 @@ func (e *Bit) String() string {
func (e *Bit) Initialize(p *Protocol) {}
+// FieldRef represents a reference to some variable in the generated code
+// with name Name.
type FieldRef struct {
Name string
}
@@ -285,6 +321,9 @@ func (e *FieldRef) Initialize(p *Protocol) {
e.Name = SrcName(p, e.Name)
}
+// EnumRef represents a reference to some enumeration field.
+// EnumKind is the "group" an EnumItem is the name of the specific enumeration
+// value inside that group.
type EnumRef struct {
EnumKind Type
EnumItem string
@@ -312,6 +351,8 @@ func (e *EnumRef) Initialize(p *Protocol) {
e.EnumItem = SrcName(p, e.EnumItem)
}
+// SumOf represents a summation of the variable in the generated code named by
+// Name. It is not currently used. (It's XKB voodoo.)
type SumOf struct {
Name string
}
diff --git a/nexgb/xgbgen/field.go b/nexgb/xgbgen/field.go
index 5041f77..725f3de 100644
--- a/nexgb/xgbgen/field.go
+++ b/nexgb/xgbgen/field.go
@@ -6,20 +6,48 @@ import (
"strings"
)
+// Field corresponds to any field described in an XML protocol description
+// file. This includes struct fields, union fields, request fields,
+// reply fields and so on.
+// To make code generation easier, fields that have types are also stored.
+// Note that not all fields support all methods defined in this interface.
+// For instance, a padding field does not have a source name.
type Field interface {
+ // Initialize sets up the source name of this field.
Initialize(p *Protocol)
+
+ // SrcName is the Go source name of this field.
SrcName() string
+
+ // XmlName is the name of this field from the XML file.
XmlName() string
+
+ // SrcType is the Go source type name of this field.
SrcType() string
+
+ // Size returns an expression that computes the size (in bytes)
+ // of this field.
Size() Size
+ // Define writes the Go code to declare this field (in a struct definition).
Define(c *Context)
+
+ // Read writes the Go code to convert a byte slice to a Go value
+ // of this field.
+ // 'prefix' is the prefix of the name of the Go value.
Read(c *Context, prefix string)
+
+ // Write writes the Go code to convert a Go value to a byte slice of
+ // this field.
+ // 'prefix' is the prefix of the name of the Go value.
Write(c *Context, prefix string)
}
func (pad *PadField) Initialize(p *Protocol) {}
+// PadField represents any type of padding. It is omitted from
+// definitions, but is used in Read/Write to increment the buffer index.
+// It is also used in size calculation.
type PadField struct {
Bytes uint
}
@@ -40,6 +68,8 @@ func (p *PadField) Size() Size {
return newFixedSize(p.Bytes)
}
+// SingleField represents most of the fields in an XML protocol description.
+// It corresponds to any single value.
type SingleField struct {
srcName string
xmlName string
@@ -67,6 +97,7 @@ func (f *SingleField) Size() Size {
return f.Type.Size()
}
+// ListField represents a list of values.
type ListField struct {
srcName string
xmlName string
@@ -89,6 +120,9 @@ func (f *ListField) SrcType() string {
return fmt.Sprintf("[]%s", f.Type.SrcName())
}
+// Length computes the *number* of values in a list.
+// If this ListField does not have any length expression, we throw our hands
+// up and simply compute the 'len' of the field name of this list.
func (f *ListField) Length() Size {
if f.LengthExpr == nil {
return newExpressionSize(&Function{
@@ -101,6 +135,12 @@ func (f *ListField) Length() Size {
return newExpressionSize(f.LengthExpr)
}
+// Size computes the *size* of a list (in bytes).
+// It it typically a simple matter of multiplying the length of the list by
+// the size of the type of the list.
+// But if it's a list of struct where the struct has a list field, we use a
+// special function written in go_struct.go to compute the size (since the
+// size in this case can only be computed recursively).
func (f *ListField) Size() Size {
simpleLen := &Function{
Name: "pad",
@@ -120,11 +160,6 @@ func (f *ListField) Size() Size {
}
case *Union:
return newExpressionSize(simpleLen)
- // sizeFun := &Function{
- // Name: fmt.Sprintf("%sListSize", f.Type.SrcName()),
- // Expr: &FieldRef{Name: f.SrcName()},
- // }
- // return newExpressionSize(sizeFun)
case *Base:
return newExpressionSize(simpleLen)
case *Resource:
@@ -145,10 +180,14 @@ func (f *ListField) Initialize(p *Protocol) {
}
}
+// LocalField is exactly the same as a regular SingleField, except it isn't
+// sent over the wire. (i.e., it's probably used to compute an ExprField).
type LocalField struct {
*SingleField
}
+// ExprField is a field that is not parameterized, but is computed from values
+// of other fields.
type ExprField struct {
srcName string
xmlName string
@@ -178,6 +217,9 @@ func (f *ExprField) Initialize(p *Protocol) {
f.Expr.Initialize(p)
}
+// ValueField represents two fields in one: a mask and a list of 4-byte
+// integers. The mask specifies which kinds of values are in the list.
+// (i.e., See ConfigureWindow, CreateWindow, ChangeWindowAttributes, etc.)
type ValueField struct {
Parent interface{}
MaskType Type
@@ -197,6 +239,10 @@ func (f *ValueField) SrcType() string {
return f.MaskType.SrcName()
}
+// Size computes the size in bytes of the combination of the mask and list
+// in this value field.
+// The expression to compute this looks complicated, but it's really just
+// the number of bits set in the mask multiplied 4 (and padded of course).
func (f *ValueField) Size() Size {
maskSize := f.MaskType.Size()
listSize := newExpressionSize(&Function{
@@ -234,6 +280,8 @@ func (f *ValueField) Initialize(p *Protocol) {
f.ListName = SrcName(p, f.ListName)
}
+// SwitchField represents a 'switch' element in the XML protocol description
+// file. It is not currently used. (i.e., it is XKB voodoo.)
type SwitchField struct {
Name string
Expr Expression
@@ -270,6 +318,8 @@ func (f *SwitchField) Initialize(p *Protocol) {
}
}
+// Bitcase represents a single bitcase inside a switch expression.
+// It is not currently used. (i.e., it's XKB voodoo.)
type Bitcase struct {
Fields []Field
Expr Expression
diff --git a/nexgb/xgbgen/misc.go b/nexgb/xgbgen/misc.go
index 13c4cc2..85d788f 100644
--- a/nexgb/xgbgen/misc.go
+++ b/nexgb/xgbgen/misc.go
@@ -7,7 +7,7 @@ import (
// AllCaps is a regex to test if a string identifier is made of
// all upper case letters.
-var AllCaps = regexp.MustCompile("^[A-Z0-9]+$")
+var allCaps = regexp.MustCompile("^[A-Z0-9]+$")
// popCount counts number of bits 'set' in mask.
func popCount(mask uint) uint {
@@ -30,7 +30,7 @@ func pad(n int) int {
// 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) {
+ if allCaps.MatchString(s) {
return strings.Title(strings.ToLower(s))
}
diff --git a/nexgb/xgbgen/protocol.go b/nexgb/xgbgen/protocol.go
new file mode 100644
index 0000000..505b400
--- /dev/null
+++ b/nexgb/xgbgen/protocol.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "strings"
+)
+
+// Protocol is a type that encapsulates all information about one
+// particular XML file. It also contains links to other protocol types
+// if this protocol imports other other extensions. The import relationship
+// is recursive.
+type Protocol struct {
+ Name string
+ ExtXName string
+ ExtName string
+ MajorVersion string
+ MinorVersion string
+
+ Imports []*Protocol
+ Types []Type
+ Requests []*Request
+}
+
+// Initialize traverses all structures, looks for 'Translation' type,
+// and looks up the real type in the namespace. It also sets the source
+// name for all relevant fields/structures.
+// This is necessary because we don't traverse the XML in order initially.
+func (p *Protocol) Initialize() {
+ for _, typ := range p.Types {
+ typ.Initialize(p)
+ }
+ for _, req := range p.Requests {
+ req.Initialize(p)
+ }
+}
+
+// isExt returns true if this protocol is an extension.
+// i.e., it's name isn't "xproto".
+func (p *Protocol) isExt() bool {
+ return strings.ToLower(p.Name) == "xproto"
+}
+
diff --git a/nexgb/xgbgen/representation.go b/nexgb/xgbgen/request_reply.go
index 62a3eb2..7cd2859 100644
--- a/nexgb/xgbgen/representation.go
+++ b/nexgb/xgbgen/request_reply.go
@@ -7,40 +7,19 @@ import (
"unicode"
)
-type Protocol struct {
- Name string
- ExtXName string
- ExtName string
- MajorVersion string
- MinorVersion string
-
- Imports []*Protocol
- Types []Type
- Requests []*Request
-}
-
-// Initialize traverses all structures, looks for 'Translation' type,
-// and looks up the real type in the namespace. It also sets the source
-// name for all relevant fields/structures.
-// This is necessary because we don't traverse the XML in order initially.
-func (p *Protocol) Initialize() {
- for _, typ := range p.Types {
- typ.Initialize(p)
- }
- for _, req := range p.Requests {
- req.Initialize(p)
- }
-}
-
+// Request represents all XML 'request' nodes.
+// If the request doesn't have a reply, Reply is nil.
type Request struct {
- srcName string
- xmlName string
+ srcName string // The Go name of this request.
+ xmlName string // The XML name of this request.
Opcode int
- Combine bool
- Fields []Field
- Reply *Reply
+ Combine bool // Not currently used.
+ Fields []Field // All fields in the request.
+ Reply *Reply // A reply, if one exists for this request.
}
+// Initialize creates the proper Go source name for this request.
+// It also initializes the reply if one exists, and all fields in this request.
func (r *Request) Initialize(p *Protocol) {
r.srcName = SrcName(p, r.xmlName)
if p.Name != "xproto" {
@@ -63,6 +42,9 @@ func (r *Request) XmlName() string {
return r.xmlName
}
+// ReplyName gets the Go source name of the function that generates a
+// reply type from a slice of bytes.
+// The generated function is not currently exported.
func (r *Request) ReplyName() string {
if r.Reply == nil {
log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
@@ -73,6 +55,8 @@ func (r *Request) ReplyName() string {
return fmt.Sprintf("%sReply", lower)
}
+// ReplyTypeName gets the Go source name of the type holding all reply data
+// for this request.
func (r *Request) ReplyTypeName() string {
if r.Reply == nil {
log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
@@ -81,12 +65,17 @@ func (r *Request) ReplyTypeName() string {
return fmt.Sprintf("%sReply", r.SrcName())
}
+// ReqName gets the Go source name of the function that generates a byte
+// slice from a list of parameters.
+// The generated function is not currently exported.
func (r *Request) ReqName() string {
name := r.SrcName()
lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
return fmt.Sprintf("%sRequest", lower)
}
+// CookieName gets the Go source name of the type that holds cookies for
+// this request.
func (r *Request) CookieName() string {
return fmt.Sprintf("%sCookie", r.SrcName())
}
@@ -99,6 +88,11 @@ func (r *Request) CookieName() string {
func (r *Request) Size(c *Context) Size {
size := newFixedSize(0)
+ // If this is a core protocol request, we squeeze in an extra byte of
+ // data (from the fields below) between the opcode and the size of the
+ // request. In an extension request, this byte is always occupied
+ // by the opcode of the request (while the first byte is always occupied
+ // by the opcode of the extension).
if c.protocol.Name == "xproto" {
size = size.Add(newFixedSize(3))
} else {
@@ -107,7 +101,7 @@ func (r *Request) Size(c *Context) Size {
for _, field := range r.Fields {
switch field.(type) {
- case *LocalField:
+ case *LocalField: // local fields don't go over the wire
continue
case *SingleField:
// mofos!!!
@@ -126,10 +120,16 @@ func (r *Request) Size(c *Context) Size {
})
}
+// Reply encapsulates the fields associated with a 'reply' element.
type Reply struct {
Fields []Field
}
+// Size gets the number of bytes in this request's reply.
+// A reply always has at least 7 bytes:
+// 1 byte: A reply discriminant (first byte set to 1)
+// 2 bytes: A sequence number
+// 4 bytes: Number of additional bytes in 4-byte units past initial 32 bytes.
func (r *Reply) Size() Size {
size := newFixedSize(0)
diff --git a/nexgb/xgbgen/size.go b/nexgb/xgbgen/size.go
index 70edb8f..d8d3ac3 100644
--- a/nexgb/xgbgen/size.go
+++ b/nexgb/xgbgen/size.go
@@ -1,21 +1,30 @@
package main
+// Size corresponds to an expression that represents the number of bytes
+// in some *thing*. Generally, sizes are used to allocate buffers and to
+// inform X how big requests are.
+// Size is basically a thin layer over an Expression that yields easy methods
+// for adding and multiplying sizes.
type Size struct {
Expression
}
+// newFixedSize creates a new Size with some fixed and known value.
func newFixedSize(fixed uint) Size {
return Size{&Value{v: fixed}}
}
+// newExpressionSize creates a new Size with some expression.
func newExpressionSize(variable Expression) Size {
return Size{variable}
}
+// Add adds s1 and s2 and returns a new Size.
func (s1 Size) Add(s2 Size) Size {
return Size{newBinaryOp("+", s1, s2)}
}
+// Multiply mupltiplies s1 and s2 and returns a new Size.
func (s1 Size) Multiply(s2 Size) Size {
return Size{newBinaryOp("*", s1, s2)}
}
diff --git a/nexgb/xgbgen/xml.go b/nexgb/xgbgen/xml.go
index 1b2f89a..df21433 100644
--- a/nexgb/xgbgen/xml.go
+++ b/nexgb/xgbgen/xml.go
@@ -18,19 +18,19 @@ type XML struct {
// Types for all top-level elements.
// First are the simple ones.
Imports XMLImports `xml:"import"`
- Enums XMLEnums `xml:"enum"`
- Xids XMLXids `xml:"xidtype"`
- XidUnions XMLXids `xml:"xidunion"`
- TypeDefs XMLTypeDefs `xml:"typedef"`
- EventCopies XMLEventCopies `xml:"eventcopy"`
- ErrorCopies XMLErrorCopies `xml:"errorcopy"`
+ Enums []*XMLEnum `xml:"enum"`
+ Xids []*XMLXid `xml:"xidtype"`
+ XidUnions []*XMLXid `xml:"xidunion"`
+ TypeDefs []*XMLTypeDef `xml:"typedef"`
+ EventCopies []*XMLEventCopy `xml:"eventcopy"`
+ ErrorCopies []*XMLErrorCopy `xml:"errorcopy"`
// Here are the complex ones, i.e., anything with "structure contents"
- Structs XMLStructs `xml:"struct"`
- Unions XMLUnions `xml:"union"`
- Requests XMLRequests `xml:"request"`
- Events XMLEvents `xml:"event"`
- Errors XMLErrors `xml:"error"`
+ Structs []*XMLStruct `xml:"struct"`
+ Unions []*XMLUnion `xml:"union"`
+ Requests []*XMLRequest `xml:"request"`
+ Events []*XMLEvent `xml:"event"`
+ Errors []*XMLError `xml:"error"`
}
type XMLImports []*XMLImport
@@ -60,8 +60,6 @@ type XMLImport struct {
xml *XML `xml:"-"`
}
-type XMLEnums []XMLEnum
-
type XMLEnum struct {
Name string `xml:"name,attr"`
Items []*XMLEnumItem `xml:"item"`
@@ -72,77 +70,69 @@ type XMLEnumItem struct {
Expr *XMLExpression `xml:",any"`
}
-type XMLXids []*XMLXid
-
type XMLXid struct {
XMLName xml.Name
Name string `xml:"name,attr"`
}
-type XMLTypeDefs []*XMLTypeDef
-
type XMLTypeDef struct {
Old string `xml:"oldname,attr"`
New string `xml:"newname,attr"`
}
-type XMLEventCopies []*XMLEventCopy
-
type XMLEventCopy struct {
Name string `xml:"name,attr"`
Number int `xml:"number,attr"`
Ref string `xml:"ref,attr"`
}
-type XMLErrorCopies []*XMLErrorCopy
-
type XMLErrorCopy struct {
Name string `xml:"name,attr"`
Number int `xml:"number,attr"`
Ref string `xml:"ref,attr"`
}
-type XMLStructs []*XMLStruct
-
type XMLStruct struct {
Name string `xml:"name,attr"`
- Fields XMLFields `xml:",any"`
+ Fields []*XMLField `xml:",any"`
}
-type XMLUnions []*XMLUnion
-
type XMLUnion struct {
Name string `xml:"name,attr"`
- Fields XMLFields `xml:",any"`
+ Fields []*XMLField `xml:",any"`
}
-type XMLRequests []*XMLRequest
-
type XMLRequest struct {
Name string `xml:"name,attr"`
Opcode int `xml:"opcode,attr"`
Combine bool `xml:"combine-adjacent,attr"`
- Fields XMLFields `xml:",any"`
+ Fields []*XMLField `xml:",any"`
Reply *XMLReply `xml:"reply"`
}
type XMLReply struct {
- Fields XMLFields `xml:",any"`
+ Fields []*XMLField `xml:",any"`
}
-type XMLEvents []*XMLEvent
-
type XMLEvent struct {
Name string `xml:"name,attr"`
Number int `xml:"number,attr"`
NoSequence bool `xml:"no-sequence-number,attr"`
- Fields XMLFields `xml:",any"`
+ Fields []*XMLField `xml:",any"`
}
-type XMLErrors []*XMLError
-
type XMLError struct {
Name string `xml:"name,attr"`
Number int `xml:"number,attr"`
- Fields XMLFields `xml:",any"`
+ Fields []*XMLField `xml:",any"`
+}
+
+type XMLExpression struct {
+ XMLName xml.Name
+
+ Exprs []*XMLExpression `xml:",any"`
+
+ Data string `xml:",chardata"`
+ Op string `xml:"op,attr"`
+ Ref string `xml:"ref,attr"`
}
diff --git a/nexgb/xgbgen/xml_expression.go b/nexgb/xgbgen/xml_expression.go
deleted file mode 100644
index 2989668..0000000
--- a/nexgb/xgbgen/xml_expression.go
+++ /dev/null
@@ -1,160 +0,0 @@
-package main
-
-import (
- "encoding/xml"
- "fmt"
- "log"
- "strconv"
-)
-
-type XMLExpression struct {
- XMLName xml.Name
-
- Exprs []*XMLExpression `xml:",any"`
-
- Data string `xml:",chardata"`
- Op string `xml:"op,attr"`
- Ref string `xml:"ref,attr"`
-}
-
-func newValueExpression(v uint) *XMLExpression {
- return &XMLExpression{
- XMLName: xml.Name{Local: "value"},
- Data: fmt.Sprintf("%d", v),
- }
-}
-
-// String is for debugging. For actual use, please use 'Morph'.
-func (e *XMLExpression) 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 *XMLExpression) 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 *XMLExpression) BinaryOp(oprnd1, oprnd2 *XMLExpression) *XMLExpression {
- 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(oprnd1.Eval() + oprnd2.Eval())
- case "-":
- return wrap(oprnd1.Eval() + oprnd2.Eval())
- case "*":
- return wrap(oprnd1.Eval() * oprnd2.Eval())
- case "/":
- return wrap(oprnd1.Eval() / oprnd2.Eval())
- case "&amp;":
- return wrap(oprnd1.Eval() & oprnd2.Eval())
- case "&lt;&lt;":
- return wrap(oprnd1.Eval() << oprnd2.Eval())
- }
-
- log.Panicf("Invalid binary operator '%s' for '%s' expression.",
- e.Op, e.XMLName.Local)
- panic("unreachable")
-}
-
-func (e *XMLExpression) UnaryOp(oprnd *XMLExpression) *XMLExpression {
- 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(^oprnd.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
index 991141b..fe6c5d5 100644
--- a/nexgb/xgbgen/xml_fields.go
+++ b/nexgb/xgbgen/xml_fields.go
@@ -1,31 +1,10 @@
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 XMLFields []*XMLField
-
type XMLField struct {
XMLName xml.Name
@@ -47,7 +26,7 @@ type XMLField struct {
ValueListName string `xml:"value-list-name,attr"`
// For 'switch' element.
- Bitcases XMLBitcases `xml:"bitcase"`
+ Bitcases []*XMLBitcase `xml:"bitcase"`
// I don't know which elements these are for. The documentation is vague.
// They also seem to be completely optional.
@@ -56,41 +35,6 @@ type XMLField struct {
OptAltEnum string `xml:"altenum,attr"`
}
-// String is for debugging purposes.
-func (f *XMLField) 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 XMLBitcases []*XMLBitcase
-
// 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,
@@ -100,7 +44,7 @@ type XMLBitcases []*XMLBitcase
// 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 XMLBitcase struct {
- Fields XMLFields `xml:",any"`
+ Fields []*XMLField `xml:",any"`
// All the different expressions.
// When it comes time to choose one, use the 'Expr' method.
@@ -114,17 +58,6 @@ type XMLBitcase struct {
ExprPop *XMLExpression `xml:"popcount"`
}
-// StringPrefix is for debugging purposes only.
-// StringPrefix takes a string to prefix to every extra line for formatting.
-func (b *XMLBitcase) 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 *XMLBitcase) Expr() *XMLExpression {