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"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue