diff options
Diffstat (limited to 'nexgb')
| -rw-r--r-- | nexgb/xgbgen/context.go | 2 | ||||
| -rw-r--r-- | nexgb/xgbgen/doc.go | 74 | ||||
| -rw-r--r-- | nexgb/xgbgen/expression.go | 41 | ||||
| -rw-r--r-- | nexgb/xgbgen/field.go | 60 | ||||
| -rw-r--r-- | nexgb/xgbgen/misc.go | 4 | ||||
| -rw-r--r-- | nexgb/xgbgen/protocol.go | 41 | ||||
| -rw-r--r-- | nexgb/xgbgen/request_reply.go (renamed from nexgb/xgbgen/representation.go) | 62 | ||||
| -rw-r--r-- | nexgb/xgbgen/size.go | 9 | ||||
| -rw-r--r-- | nexgb/xgbgen/xml.go | 64 | ||||
| -rw-r--r-- | nexgb/xgbgen/xml_expression.go | 160 | ||||
| -rw-r--r-- | nexgb/xgbgen/xml_fields.go | 71 | 
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 "&": -		return wrap(oprnd1.Eval() & oprnd2.Eval()) -	case "<<": -		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 { | 
