added documentation and did some slight restructuring. it's party time.
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								nexgb/xgbgen/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								nexgb/xgbgen/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								nexgb/xgbgen/protocol.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								nexgb/xgbgen/protocol.go
									
									
									
									
									
										Normal file
									
								
							@@ -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"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -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)
 | 
			
		||||
 | 
			
		||||
@@ -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)}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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")
 | 
			
		||||
}
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user