haven/nexgb/xgbgen/field.go
Přemysl Janouch 3e9ed4eac6
xgbgen: process <doc> elements
Most of XCB documentation now ends up in Go sources,
although the end result is of mixed quality.
2018-09-30 17:34:26 +02:00

403 lines
9.6 KiB
Go

package main
import (
"fmt"
"log"
"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
Align uint16
}
func (p *PadField) SrcName() string {
panic("illegal to take source name of a pad field")
}
func (p *PadField) XmlName() string {
panic("illegal to take XML name of a pad field")
}
func (f *PadField) SrcType() string {
panic("it is illegal to call SrcType on a PadField field")
}
func (p *PadField) Size() Size {
if p.Align > 0 {
return newFixedSize(uint(p.Align), false)
} else {
return newFixedSize(p.Bytes, true)
}
}
type RequiredStartAlign struct {
}
func (f *RequiredStartAlign) Initialize(p *Protocol) {}
func (f *RequiredStartAlign) SrcName() string {
panic("illegal to take source name of a required_start_align field")
}
func (f *RequiredStartAlign) XmlName() string {
panic("illegal to take XML name of a required_start_align field")
}
func (f *RequiredStartAlign) SrcType() string {
panic("it is illegal to call SrcType on a required_start_align field")
}
func (f *RequiredStartAlign) Size() Size {
return newFixedSize(0, true)
}
func (f *RequiredStartAlign) Define(c *Context) {}
func (f *RequiredStartAlign) Read(c *Context, prefix string) {}
func (f *RequiredStartAlign) Write(c *Context, prefix string) {}
// SingleField represents most of the fields in an XML protocol description.
// It corresponds to any single value.
type SingleField struct {
srcName string
xmlName string
Type Type
Comment string
}
func (f *SingleField) Initialize(p *Protocol) {
f.srcName = SrcName(p, f.XmlName())
f.Type = f.Type.(*Translation).RealType(p)
}
func (f *SingleField) SrcName() string {
if f.srcName == "Bytes" {
return "Bytes_"
}
return f.srcName
}
func (f *SingleField) XmlName() string {
return f.xmlName
}
func (f *SingleField) SrcType() string {
return f.Type.SrcName()
}
func (f *SingleField) Size() Size {
return f.Type.Size()
}
// ListField represents a list of values.
type ListField struct {
srcName string
xmlName string
Type Type
LengthExpr Expression
}
func (f *ListField) SrcName() string {
return f.srcName
}
func (f *ListField) XmlName() string {
return f.xmlName
}
func (f *ListField) SrcType() string {
if strings.ToLower(f.Type.XmlName()) == "char" {
return fmt.Sprintf("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{
Name: "len",
Expr: &FieldRef{
Name: f.SrcName(),
},
}, true)
}
return newExpressionSize(f.LengthExpr, true)
}
// 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 {
elsz := f.Type.Size()
simpleLen := &Padding{
Expr: newBinaryOp("*", f.Length().Expression, elsz.Expression),
}
switch field := f.Type.(type) {
case *Struct:
if field.HasList() {
sizeFun := &Function{
Name: fmt.Sprintf("%sListSize", f.Type.SrcName()),
Expr: &FieldRef{Name: f.SrcName()},
}
return newExpressionSize(sizeFun, elsz.exact)
} else {
return newExpressionSize(simpleLen, elsz.exact)
}
case *Union:
return newExpressionSize(simpleLen, elsz.exact)
case *Base:
return newExpressionSize(simpleLen, elsz.exact)
case *Resource:
return newExpressionSize(simpleLen, elsz.exact)
case *TypeDef:
return newExpressionSize(simpleLen, elsz.exact)
default:
log.Panicf("Cannot compute list size with type '%T'.", f.Type)
}
panic("unreachable")
}
func (f *ListField) Initialize(p *Protocol) {
f.srcName = SrcName(p, f.XmlName())
f.Type = f.Type.(*Translation).RealType(p)
if f.LengthExpr != nil {
f.LengthExpr.Initialize(p)
}
}
// 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
Type Type
Expr Expression
}
func (f *ExprField) SrcName() string {
return f.srcName
}
func (f *ExprField) XmlName() string {
return f.xmlName
}
func (f *ExprField) SrcType() string {
return f.Type.SrcName()
}
func (f *ExprField) Size() Size {
return f.Type.Size()
}
func (f *ExprField) Initialize(p *Protocol) {
f.srcName = SrcName(p, f.XmlName())
f.Type = f.Type.(*Translation).RealType(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 {
Parent interface{}
MaskType Type
MaskName string
ListName string
MaskComment string
ListComment string
}
func (f *ValueField) SrcName() string {
panic("it is illegal to call SrcName on a ValueField field")
}
func (f *ValueField) XmlName() string {
panic("it is illegal to call XmlName on a ValueField field")
}
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{
Name: "xgb.Pad",
Expr: &BinaryOp{
Op: "*",
Expr1: &Value{v: 4},
Expr2: &PopCount{
Expr: &Function{
Name: "int",
Expr: &FieldRef{
Name: f.MaskName,
},
},
},
},
}, true)
return maskSize.Add(listSize)
}
func (f *ValueField) ListLength() Size {
return newExpressionSize(&PopCount{
Expr: &Function{
Name: "int",
Expr: &FieldRef{
Name: f.MaskName,
},
},
}, true)
}
func (f *ValueField) Initialize(p *Protocol) {
f.MaskType = f.MaskType.(*Translation).RealType(p)
f.MaskName = SrcName(p, f.MaskName)
f.ListName = SrcName(p, f.ListName)
}
// SwitchField represents a 'switch' element in the XML protocol description
// file.
// Currently we translate this to a slice of uint32 and let the user sort
// through it.
type SwitchField struct {
xmlName string
Name string
MaskName string
Expr Expression
Bitcases []*Bitcase
Comment string
}
func (f *SwitchField) SrcName() string {
return f.Name
}
func (f *SwitchField) XmlName() string {
return f.xmlName
}
func (f *SwitchField) SrcType() string {
return "[]uint32"
}
func (f *SwitchField) Size() Size {
// TODO: size expression used here is not correct unless every element of
// the switch is 32 bit long. This assumption holds for xproto but may not
// hold for other protocols (xkb?)
listSize := newExpressionSize(&Function{
Name: "xgb.Pad",
Expr: &BinaryOp{
Op: "*",
Expr1: &Value{v: 4},
Expr2: &PopCount{
Expr: &Function{
Name: "int",
Expr: &FieldRef{
Name: f.MaskName,
},
},
},
},
}, true)
return listSize
}
func (f *SwitchField) ListLength() Size {
return newExpressionSize(&PopCount{
Expr: &Function{
Name: "int",
Expr: &FieldRef{
Name: f.MaskName,
},
},
}, true)
}
func (f *SwitchField) Initialize(p *Protocol) {
f.xmlName = f.Name
f.Name = SrcName(p, f.Name)
f.Expr.Initialize(p)
fieldref, ok := f.Expr.(*FieldRef)
if !ok {
panic("switch field's expression not a fieldref")
}
f.MaskName = SrcName(p, fieldref.Name)
for _, bitcase := range f.Bitcases {
bitcase.Expr.Initialize(p)
for _, field := range bitcase.Fields {
field.Initialize(p)
}
}
}
// 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
}