added documentation and did some slight restructuring. it's party time.
This commit is contained in:
parent
99bc76de54
commit
18b2d420b0
|
@ -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
|
||||||
|
|
|
@ -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"
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
"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)
|
||||||
|
|
|
@ -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)}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
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 {
|
||||||
|
|
Loading…
Reference in New Issue