added documentation and did some slight restructuring. it's party time.

This commit is contained in:
Andrew Gallant (Ocelot) 2012-05-06 02:21:31 -04:00
parent 99bc76de54
commit 18b2d420b0
11 changed files with 284 additions and 304 deletions

View File

@ -8,6 +8,8 @@ import (
"time" "time"
) )
// Context represents the protocol we're converting to Go, and a writer
// buffer to write the Go source to.
type Context struct { type Context struct {
protocol *Protocol protocol *Protocol
out *bytes.Buffer out *bytes.Buffer

74
nexgb/xgbgen/doc.go Normal file
View 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

View File

@ -5,11 +5,32 @@ import (
"log" "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 { 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 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 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 Reduce(prefix string) string
// String is an alias for Reduce("")
String() string String() string
// Initialize makes sure all names in this expression and any subexpressions
// have been translated to Go source names.
Initialize(p *Protocol) Initialize(p *Protocol)
} }
@ -41,12 +62,17 @@ func (e *Function) Initialize(p *Protocol) {
e.Expr.Initialize(p) 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 { type BinaryOp struct {
Op string Op string
Expr1 Expression Expr1 Expression
Expr2 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 { func newBinaryOp(op string, expr1, expr2 Expression) Expression {
switch { switch {
case expr1 != nil && expr2 != nil: case expr1 != nil && expr2 != nil:
@ -124,6 +150,8 @@ func (e *BinaryOp) Initialize(p *Protocol) {
e.Expr2.Initialize(p) e.Expr2.Initialize(p)
} }
// UnaryOp is the same as BinaryOp, except it's a unary operator with only
// one sub-expression.
type UnaryOp struct { type UnaryOp struct {
Op string Op string
Expr Expression Expr Expression
@ -158,6 +186,8 @@ func (e *UnaryOp) Initialize(p *Protocol) {
e.Expr.Initialize(p) e.Expr.Initialize(p)
} }
// Padding represents the application of the 'pad' function to some
// sub-expression.
type Padding struct { type Padding struct {
Expr Expression Expr Expression
} }
@ -185,6 +215,8 @@ func (e *Padding) Initialize(p *Protocol) {
e.Expr.Initialize(p) e.Expr.Initialize(p)
} }
// PopCount represents the application of the 'PopCount' function to
// some sub-expression.
type PopCount struct { type PopCount struct {
Expr Expression Expr Expression
} }
@ -212,6 +244,7 @@ func (e *PopCount) Initialize(p *Protocol) {
e.Expr.Initialize(p) e.Expr.Initialize(p)
} }
// Value represents some constant integer.
type Value struct { type Value struct {
v uint v uint
} }
@ -234,6 +267,7 @@ func (e *Value) String() string {
func (e *Value) Initialize(p *Protocol) {} func (e *Value) Initialize(p *Protocol) {}
// Bit represents some bit whose value is computed by '1 << bit'.
type Bit struct { type Bit struct {
b uint b uint
} }
@ -256,6 +290,8 @@ func (e *Bit) String() string {
func (e *Bit) Initialize(p *Protocol) {} func (e *Bit) Initialize(p *Protocol) {}
// FieldRef represents a reference to some variable in the generated code
// with name Name.
type FieldRef struct { type FieldRef struct {
Name string Name string
} }
@ -285,6 +321,9 @@ func (e *FieldRef) Initialize(p *Protocol) {
e.Name = SrcName(p, e.Name) 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 { type EnumRef struct {
EnumKind Type EnumKind Type
EnumItem string EnumItem string
@ -312,6 +351,8 @@ func (e *EnumRef) Initialize(p *Protocol) {
e.EnumItem = SrcName(p, e.EnumItem) 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 { type SumOf struct {
Name string Name string
} }

View File

@ -6,20 +6,48 @@ import (
"strings" "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 { type Field interface {
// Initialize sets up the source name of this field.
Initialize(p *Protocol) Initialize(p *Protocol)
// SrcName is the Go source name of this field.
SrcName() string SrcName() string
// XmlName is the name of this field from the XML file.
XmlName() string XmlName() string
// SrcType is the Go source type name of this field.
SrcType() string SrcType() string
// Size returns an expression that computes the size (in bytes)
// of this field.
Size() Size Size() Size
// Define writes the Go code to declare this field (in a struct definition).
Define(c *Context) 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) 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) Write(c *Context, prefix string)
} }
func (pad *PadField) Initialize(p *Protocol) {} 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 { type PadField struct {
Bytes uint Bytes uint
} }
@ -40,6 +68,8 @@ func (p *PadField) Size() Size {
return newFixedSize(p.Bytes) return newFixedSize(p.Bytes)
} }
// SingleField represents most of the fields in an XML protocol description.
// It corresponds to any single value.
type SingleField struct { type SingleField struct {
srcName string srcName string
xmlName string xmlName string
@ -67,6 +97,7 @@ func (f *SingleField) Size() Size {
return f.Type.Size() return f.Type.Size()
} }
// ListField represents a list of values.
type ListField struct { type ListField struct {
srcName string srcName string
xmlName string xmlName string
@ -89,6 +120,9 @@ func (f *ListField) SrcType() string {
return fmt.Sprintf("[]%s", f.Type.SrcName()) 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 { func (f *ListField) Length() Size {
if f.LengthExpr == nil { if f.LengthExpr == nil {
return newExpressionSize(&Function{ return newExpressionSize(&Function{
@ -101,6 +135,12 @@ func (f *ListField) Length() Size {
return newExpressionSize(f.LengthExpr) 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 { func (f *ListField) Size() Size {
simpleLen := &Function{ simpleLen := &Function{
Name: "pad", Name: "pad",
@ -120,11 +160,6 @@ func (f *ListField) Size() Size {
} }
case *Union: case *Union:
return newExpressionSize(simpleLen) return newExpressionSize(simpleLen)
// sizeFun := &Function{
// Name: fmt.Sprintf("%sListSize", f.Type.SrcName()),
// Expr: &FieldRef{Name: f.SrcName()},
// }
// return newExpressionSize(sizeFun)
case *Base: case *Base:
return newExpressionSize(simpleLen) return newExpressionSize(simpleLen)
case *Resource: 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 { type LocalField struct {
*SingleField *SingleField
} }
// ExprField is a field that is not parameterized, but is computed from values
// of other fields.
type ExprField struct { type ExprField struct {
srcName string srcName string
xmlName string xmlName string
@ -178,6 +217,9 @@ func (f *ExprField) Initialize(p *Protocol) {
f.Expr.Initialize(p) 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 { type ValueField struct {
Parent interface{} Parent interface{}
MaskType Type MaskType Type
@ -197,6 +239,10 @@ func (f *ValueField) SrcType() string {
return f.MaskType.SrcName() 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 { func (f *ValueField) Size() Size {
maskSize := f.MaskType.Size() maskSize := f.MaskType.Size()
listSize := newExpressionSize(&Function{ listSize := newExpressionSize(&Function{
@ -234,6 +280,8 @@ func (f *ValueField) Initialize(p *Protocol) {
f.ListName = SrcName(p, f.ListName) 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 { type SwitchField struct {
Name string Name string
Expr Expression 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 { type Bitcase struct {
Fields []Field Fields []Field
Expr Expression Expr Expression

View File

@ -7,7 +7,7 @@ import (
// AllCaps is a regex to test if a string identifier is made of // AllCaps is a regex to test if a string identifier is made of
// all upper case letters. // 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. // popCount counts number of bits 'set' in mask.
func popCount(mask uint) uint { func popCount(mask uint) uint {
@ -30,7 +30,7 @@ func pad(n int) int {
// first letter of each chunk, and smushes'em back together. // first letter of each chunk, and smushes'em back together.
func splitAndTitle(s string) string { func splitAndTitle(s string) string {
// If the string is all caps, lower it and capitalize first letter. // 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)) return strings.Title(strings.ToLower(s))
} }

41
nexgb/xgbgen/protocol.go Normal file
View 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"
}

View File

@ -7,40 +7,19 @@ import (
"unicode" "unicode"
) )
type Protocol struct { // Request represents all XML 'request' nodes.
Name string // If the request doesn't have a reply, Reply is nil.
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)
}
}
type Request struct { type Request struct {
srcName string srcName string // The Go name of this request.
xmlName string xmlName string // The XML name of this request.
Opcode int Opcode int
Combine bool Combine bool // Not currently used.
Fields []Field Fields []Field // All fields in the request.
Reply *Reply 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) { func (r *Request) Initialize(p *Protocol) {
r.srcName = SrcName(p, r.xmlName) r.srcName = SrcName(p, r.xmlName)
if p.Name != "xproto" { if p.Name != "xproto" {
@ -63,6 +42,9 @@ func (r *Request) XmlName() string {
return r.xmlName 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 { func (r *Request) ReplyName() string {
if r.Reply == nil { if r.Reply == nil {
log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.", 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) 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 { func (r *Request) ReplyTypeName() string {
if r.Reply == nil { if r.Reply == nil {
log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.", 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()) 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 { func (r *Request) ReqName() string {
name := r.SrcName() name := r.SrcName()
lower := string(unicode.ToLower(rune(name[0]))) + name[1:] lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
return fmt.Sprintf("%sRequest", lower) 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 { func (r *Request) CookieName() string {
return fmt.Sprintf("%sCookie", r.SrcName()) return fmt.Sprintf("%sCookie", r.SrcName())
} }
@ -99,6 +88,11 @@ func (r *Request) CookieName() string {
func (r *Request) Size(c *Context) Size { func (r *Request) Size(c *Context) Size {
size := newFixedSize(0) 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" { if c.protocol.Name == "xproto" {
size = size.Add(newFixedSize(3)) size = size.Add(newFixedSize(3))
} else { } else {
@ -107,7 +101,7 @@ func (r *Request) Size(c *Context) Size {
for _, field := range r.Fields { for _, field := range r.Fields {
switch field.(type) { switch field.(type) {
case *LocalField: case *LocalField: // local fields don't go over the wire
continue continue
case *SingleField: case *SingleField:
// mofos!!! // mofos!!!
@ -126,10 +120,16 @@ func (r *Request) Size(c *Context) Size {
}) })
} }
// Reply encapsulates the fields associated with a 'reply' element.
type Reply struct { type Reply struct {
Fields []Field 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 { func (r *Reply) Size() Size {
size := newFixedSize(0) size := newFixedSize(0)

View File

@ -1,21 +1,30 @@
package main 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 { type Size struct {
Expression Expression
} }
// newFixedSize creates a new Size with some fixed and known value.
func newFixedSize(fixed uint) Size { func newFixedSize(fixed uint) Size {
return Size{&Value{v: fixed}} return Size{&Value{v: fixed}}
} }
// newExpressionSize creates a new Size with some expression.
func newExpressionSize(variable Expression) Size { func newExpressionSize(variable Expression) Size {
return Size{variable} return Size{variable}
} }
// Add adds s1 and s2 and returns a new Size.
func (s1 Size) Add(s2 Size) Size { func (s1 Size) Add(s2 Size) Size {
return Size{newBinaryOp("+", s1, s2)} return Size{newBinaryOp("+", s1, s2)}
} }
// Multiply mupltiplies s1 and s2 and returns a new Size.
func (s1 Size) Multiply(s2 Size) Size { func (s1 Size) Multiply(s2 Size) Size {
return Size{newBinaryOp("*", s1, s2)} return Size{newBinaryOp("*", s1, s2)}
} }

View File

@ -18,19 +18,19 @@ type XML struct {
// Types for all top-level elements. // Types for all top-level elements.
// First are the simple ones. // First are the simple ones.
Imports XMLImports `xml:"import"` Imports XMLImports `xml:"import"`
Enums XMLEnums `xml:"enum"` Enums []*XMLEnum `xml:"enum"`
Xids XMLXids `xml:"xidtype"` Xids []*XMLXid `xml:"xidtype"`
XidUnions XMLXids `xml:"xidunion"` XidUnions []*XMLXid `xml:"xidunion"`
TypeDefs XMLTypeDefs `xml:"typedef"` TypeDefs []*XMLTypeDef `xml:"typedef"`
EventCopies XMLEventCopies `xml:"eventcopy"` EventCopies []*XMLEventCopy `xml:"eventcopy"`
ErrorCopies XMLErrorCopies `xml:"errorcopy"` ErrorCopies []*XMLErrorCopy `xml:"errorcopy"`
// Here are the complex ones, i.e., anything with "structure contents" // Here are the complex ones, i.e., anything with "structure contents"
Structs XMLStructs `xml:"struct"` Structs []*XMLStruct `xml:"struct"`
Unions XMLUnions `xml:"union"` Unions []*XMLUnion `xml:"union"`
Requests XMLRequests `xml:"request"` Requests []*XMLRequest `xml:"request"`
Events XMLEvents `xml:"event"` Events []*XMLEvent `xml:"event"`
Errors XMLErrors `xml:"error"` Errors []*XMLError `xml:"error"`
} }
type XMLImports []*XMLImport type XMLImports []*XMLImport
@ -60,8 +60,6 @@ type XMLImport struct {
xml *XML `xml:"-"` xml *XML `xml:"-"`
} }
type XMLEnums []XMLEnum
type XMLEnum struct { type XMLEnum struct {
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Items []*XMLEnumItem `xml:"item"` Items []*XMLEnumItem `xml:"item"`
@ -72,77 +70,69 @@ type XMLEnumItem struct {
Expr *XMLExpression `xml:",any"` Expr *XMLExpression `xml:",any"`
} }
type XMLXids []*XMLXid
type XMLXid struct { type XMLXid struct {
XMLName xml.Name XMLName xml.Name
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
} }
type XMLTypeDefs []*XMLTypeDef
type XMLTypeDef struct { type XMLTypeDef struct {
Old string `xml:"oldname,attr"` Old string `xml:"oldname,attr"`
New string `xml:"newname,attr"` New string `xml:"newname,attr"`
} }
type XMLEventCopies []*XMLEventCopy
type XMLEventCopy struct { type XMLEventCopy struct {
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Number int `xml:"number,attr"` Number int `xml:"number,attr"`
Ref string `xml:"ref,attr"` Ref string `xml:"ref,attr"`
} }
type XMLErrorCopies []*XMLErrorCopy
type XMLErrorCopy struct { type XMLErrorCopy struct {
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Number int `xml:"number,attr"` Number int `xml:"number,attr"`
Ref string `xml:"ref,attr"` Ref string `xml:"ref,attr"`
} }
type XMLStructs []*XMLStruct
type XMLStruct struct { type XMLStruct struct {
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Fields XMLFields `xml:",any"` Fields []*XMLField `xml:",any"`
} }
type XMLUnions []*XMLUnion
type XMLUnion struct { type XMLUnion struct {
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Fields XMLFields `xml:",any"` Fields []*XMLField `xml:",any"`
} }
type XMLRequests []*XMLRequest
type XMLRequest struct { type XMLRequest struct {
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Opcode int `xml:"opcode,attr"` Opcode int `xml:"opcode,attr"`
Combine bool `xml:"combine-adjacent,attr"` Combine bool `xml:"combine-adjacent,attr"`
Fields XMLFields `xml:",any"` Fields []*XMLField `xml:",any"`
Reply *XMLReply `xml:"reply"` Reply *XMLReply `xml:"reply"`
} }
type XMLReply struct { type XMLReply struct {
Fields XMLFields `xml:",any"` Fields []*XMLField `xml:",any"`
} }
type XMLEvents []*XMLEvent
type XMLEvent struct { type XMLEvent struct {
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Number int `xml:"number,attr"` Number int `xml:"number,attr"`
NoSequence bool `xml:"no-sequence-number,attr"` NoSequence bool `xml:"no-sequence-number,attr"`
Fields XMLFields `xml:",any"` Fields []*XMLField `xml:",any"`
} }
type XMLErrors []*XMLError
type XMLError struct { type XMLError struct {
Name string `xml:"name,attr"` Name string `xml:"name,attr"`
Number int `xml:"number,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"`
} }

View File

@ -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")
}

View File

@ -1,31 +1,10 @@
package main 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 ( import (
"encoding/xml" "encoding/xml"
"fmt"
"log" "log"
"strings"
) )
type XMLFields []*XMLField
type XMLField struct { type XMLField struct {
XMLName xml.Name XMLName xml.Name
@ -47,7 +26,7 @@ type XMLField struct {
ValueListName string `xml:"value-list-name,attr"` ValueListName string `xml:"value-list-name,attr"`
// For 'switch' element. // For 'switch' element.
Bitcases XMLBitcases `xml:"bitcase"` Bitcases []*XMLBitcase `xml:"bitcase"`
// I don't know which elements these are for. The documentation is vague. // I don't know which elements these are for. The documentation is vague.
// They also seem to be completely optional. // They also seem to be completely optional.
@ -56,41 +35,6 @@ type XMLField struct {
OptAltEnum string `xml:"altenum,attr"` 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. // Bitcase represents a single expression followed by any number of fields.
// Namely, if the switch's expression (all bitcases are inside a switch), // 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, // 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. // 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? :-( // Would an '<expression>' tag have been too much to ask? :-(
type XMLBitcase struct { type XMLBitcase struct {
Fields XMLFields `xml:",any"` Fields []*XMLField `xml:",any"`
// All the different expressions. // All the different expressions.
// When it comes time to choose one, use the 'Expr' method. // When it comes time to choose one, use the 'Expr' method.
@ -114,17 +58,6 @@ type XMLBitcase struct {
ExprPop *XMLExpression `xml:"popcount"` 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. // Expr chooses the only non-nil Expr* field from Bitcase.
// Panic if there is more than one non-nil expression. // Panic if there is more than one non-nil expression.
func (b *XMLBitcase) Expr() *XMLExpression { func (b *XMLBitcase) Expr() *XMLExpression {