diff options
author | Přemysl Janouch <p@janouch.name> | 2018-09-08 16:54:17 +0200 |
---|---|---|
committer | Přemysl Janouch <p@janouch.name> | 2018-09-08 16:54:17 +0200 |
commit | 3173202cc1e08762c6e156a8fffd23269a5ddb2b (patch) | |
tree | 95c4a06f8384d41b15e9c22afac0a387de79dc51 /nexgb/xgbgen/translation.go | |
parent | 632b3ae494d45755525644fe5d04475c95aae364 (diff) | |
parent | 3906399e7c2a40fbaf355de572cf50a314083f64 (diff) | |
download | haven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.tar.gz haven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.tar.xz haven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.zip |
Merge aarzilli/xgb, branch xcb1.12 as nexgb
History has been linearized and rewritten to stay under the new
subdirectory. I want to make changes incompatible to BurntSushi/xgb.
The history begs for being thrown away entirely because of its quality
and because it doesn't cover the Google period but it is still useful
for copyright tracking.
Diffstat (limited to 'nexgb/xgbgen/translation.go')
-rw-r--r-- | nexgb/xgbgen/translation.go | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/nexgb/xgbgen/translation.go b/nexgb/xgbgen/translation.go new file mode 100644 index 0000000..d35fa88 --- /dev/null +++ b/nexgb/xgbgen/translation.go @@ -0,0 +1,427 @@ +package main + +/* + translation.go provides a 'Translate' method on every XML type that converts + the XML type into our "better" representation. + + i.e., the representation of Fields and Expressions is just too general. + We end up losing a lot of the advantages of static typing if we keep + the types that encoding/xml forces us into. + + Please see 'representation.go' for the type definitions that we're + translating to. +*/ + +import ( + "log" + "strconv" + "strings" +) + +func (xml *XML) Translate(parent *Protocol) *Protocol { + protocol := &Protocol{ + Parent: parent, + Name: xml.Header, + ExtXName: xml.ExtensionXName, + ExtName: xml.ExtensionName, + MajorVersion: xml.MajorVersion, + MinorVersion: xml.MinorVersion, + + Imports: make([]*Protocol, 0), + Types: make([]Type, 0), + Requests: make([]*Request, len(xml.Requests)), + } + + for _, imp := range xml.Imports { + if imp.xml != nil { + protocol.Imports = append(protocol.Imports, + imp.xml.Translate(protocol)) + } + } + + for xmlName, srcName := range BaseTypeMap { + newBaseType := &Base{ + srcName: srcName, + xmlName: xmlName, + size: newFixedSize(BaseTypeSizes[xmlName], true), + } + protocol.Types = append(protocol.Types, newBaseType) + } + for _, enum := range xml.Enums { + protocol.Types = append(protocol.Types, enum.Translate()) + } + for _, xid := range xml.Xids { + protocol.Types = append(protocol.Types, xid.Translate()) + } + for _, xidunion := range xml.XidUnions { + protocol.Types = append(protocol.Types, xidunion.Translate()) + } + for _, typedef := range xml.TypeDefs { + protocol.Types = append(protocol.Types, typedef.Translate()) + } + for _, s := range xml.Structs { + protocol.Types = append(protocol.Types, s.Translate()) + } + for _, union := range xml.Unions { + protocol.Types = append(protocol.Types, union.Translate()) + } + for _, ev := range xml.Events { + protocol.Types = append(protocol.Types, ev.Translate()) + } + for _, evcopy := range xml.EventCopies { + protocol.Types = append(protocol.Types, evcopy.Translate()) + } + for _, err := range xml.Errors { + protocol.Types = append(protocol.Types, err.Translate()) + } + for _, errcopy := range xml.ErrorCopies { + protocol.Types = append(protocol.Types, errcopy.Translate()) + } + + for i, request := range xml.Requests { + protocol.Requests[i] = request.Translate() + } + + // Now load all of the type and source name information. + protocol.Initialize() + + // Make sure all enums have concrete values. + for _, typ := range protocol.Types { + enum, ok := typ.(*Enum) + if !ok { + continue + } + nextValue := 0 + for _, item := range enum.Items { + if item.Expr == nil { + item.Expr = &Value{v: nextValue} + nextValue++ + } else { + nextValue = item.Expr.Eval() + 1 + } + } + } + + return protocol +} + +func (x *XMLEnum) Translate() *Enum { + enum := &Enum{ + xmlName: x.Name, + Items: make([]*EnumItem, len(x.Items)), + } + for i, item := range x.Items { + enum.Items[i] = &EnumItem{ + xmlName: item.Name, + Expr: item.Expr.Translate(), + } + } + return enum +} + +func (x *XMLXid) Translate() *Resource { + return &Resource{ + xmlName: x.Name, + } +} + +func (x *XMLTypeDef) Translate() *TypeDef { + return &TypeDef{ + xmlName: x.New, + Old: newTranslation(x.Old), + } +} + +func (x *XMLEvent) Translate() *Event { + ev := &Event{ + xmlName: x.Name, + Number: x.Number, + NoSequence: x.NoSequence, + Fields: make([]Field, 0, len(x.Fields)), + } + for _, field := range x.Fields { + if field.XMLName.Local == "doc" { + continue + } + ev.Fields = append(ev.Fields, field.Translate(ev)) + } + return ev +} + +func (x *XMLEventCopy) Translate() *EventCopy { + return &EventCopy{ + xmlName: x.Name, + Number: x.Number, + Old: newTranslation(x.Ref), + } +} + +func (x *XMLError) Translate() *Error { + err := &Error{ + xmlName: x.Name, + Number: x.Number, + Fields: make([]Field, len(x.Fields)), + } + for i, field := range x.Fields { + err.Fields[i] = field.Translate(err) + } + return err +} + +func (x *XMLErrorCopy) Translate() *ErrorCopy { + return &ErrorCopy{ + xmlName: x.Name, + Number: x.Number, + Old: newTranslation(x.Ref), + } +} + +func (x *XMLStruct) Translate() *Struct { + s := &Struct{ + xmlName: x.Name, + Fields: make([]Field, len(x.Fields)), + } + for i, field := range x.Fields { + s.Fields[i] = field.Translate(s) + } + return s +} + +func (x *XMLUnion) Translate() *Union { + u := &Union{ + xmlName: x.Name, + Fields: make([]Field, len(x.Fields)), + } + for i, field := range x.Fields { + u.Fields[i] = field.Translate(u) + } + return u +} + +func (x *XMLRequest) Translate() *Request { + r := &Request{ + xmlName: x.Name, + Opcode: x.Opcode, + Combine: x.Combine, + Fields: make([]Field, 0, len(x.Fields)), + Reply: x.Reply.Translate(), + } + for _, field := range x.Fields { + if field.XMLName.Local == "doc" || field.XMLName.Local == "fd" { + continue + } + r.Fields = append(r.Fields, field.Translate(r)) + } + + // Address bug (or legacy code) in QueryTextExtents. + // The XML protocol description references 'string_len' in the + // computation of the 'odd_length' field. However, 'string_len' is not + // defined. Therefore, let's forcefully add it as a 'local field'. + // (i.e., a parameter in the caller but does not get sent over the wire.) + if x.Name == "QueryTextExtents" { + stringLenLocal := &LocalField{&SingleField{ + xmlName: "string_len", + Type: newTranslation("CARD16"), + }} + r.Fields = append(r.Fields, stringLenLocal) + } + + return r +} + +func (x *XMLReply) Translate() *Reply { + if x == nil { + return nil + } + + r := &Reply{ + Fields: make([]Field, 0, len(x.Fields)), + } + for _, field := range x.Fields { + if field.XMLName.Local == "doc" || field.XMLName.Local == "fd" { + continue + } + r.Fields = append(r.Fields, field.Translate(r)) + } + return r +} + +func (x *XMLExpression) Translate() Expression { + if x == nil { + return nil + } + + switch x.XMLName.Local { + case "op": + if len(x.Exprs) != 2 { + log.Panicf("'op' found %d expressions; expected 2.", len(x.Exprs)) + } + return &BinaryOp{ + Op: x.Op, + Expr1: x.Exprs[0].Translate(), + Expr2: x.Exprs[1].Translate(), + } + case "unop": + if len(x.Exprs) != 1 { + log.Panicf("'unop' found %d expressions; expected 1.", len(x.Exprs)) + } + return &UnaryOp{ + Op: x.Op, + Expr: x.Exprs[0].Translate(), + } + case "popcount": + if len(x.Exprs) != 1 { + log.Panicf("'popcount' found %d expressions; expected 1.", + len(x.Exprs)) + } + return &PopCount{ + Expr: x.Exprs[0].Translate(), + } + case "value": + val, err := strconv.Atoi(strings.TrimSpace(x.Data)) + if err != nil { + log.Panicf("Could not convert '%s' in 'value' expression to int.", + x.Data) + } + return &Value{ + v: val, + } + case "bit": + bit, err := strconv.Atoi(strings.TrimSpace(x.Data)) + if err != nil { + log.Panicf("Could not convert '%s' in 'bit' expression to int.", + x.Data) + } + if bit < 0 || bit > 31 { + log.Panicf("A 'bit' literal must be in the range [0, 31], but "+ + " is %d", bit) + } + return &Bit{ + b: bit, + } + case "fieldref": + return &FieldRef{ + Name: x.Data, + } + case "enumref": + return &EnumRef{ + EnumKind: newTranslation(x.Ref), + EnumItem: x.Data, + } + case "sumof": + return &SumOf{ + Name: x.Ref, + } + } + + log.Panicf("Unrecognized tag '%s' in expression context. Expected one of "+ + "op, fieldref, value, bit, enumref, unop, sumof or popcount.", + x.XMLName.Local) + panic("unreachable") +} + +func (x *XMLField) Translate(parent interface{}) Field { + switch x.XMLName.Local { + case "pad": + return &PadField{ + Bytes: x.Bytes, + Align: x.Align, + } + case "field": + s := &SingleField{ + xmlName: x.Name, + Type: newTranslation(x.Type), + } + return s + case "list": + return &ListField{ + xmlName: x.Name, + Type: newTranslation(x.Type), + LengthExpr: x.Expr.Translate(), + } + case "localfield": + return &LocalField{&SingleField{ + xmlName: x.Name, + Type: newTranslation(x.Type), + }} + case "exprfield": + return &ExprField{ + xmlName: x.Name, + Type: newTranslation(x.Type), + Expr: x.Expr.Translate(), + } + case "valueparam": + return &ValueField{ + Parent: parent, + MaskType: newTranslation(x.ValueMaskType), + MaskName: x.ValueMaskName, + ListName: x.ValueListName, + } + case "switch": + swtch := &SwitchField{ + Name: x.Name, + Expr: x.Expr.Translate(), + Bitcases: make([]*Bitcase, len(x.Bitcases)), + } + for i, bitcase := range x.Bitcases { + swtch.Bitcases[i] = bitcase.Translate() + } + return swtch + case "required_start_align": + return &RequiredStartAlign{} + } + + log.Panicf("Unrecognized field element: %s", x.XMLName.Local) + panic("unreachable") +} + +func (x *XMLBitcase) Translate() *Bitcase { + b := &Bitcase{ + Expr: x.Expr().Translate(), + Fields: make([]Field, len(x.Fields)), + } + for i, field := range x.Fields { + b.Fields[i] = field.Translate(b) + } + return b +} + +// SrcName is used to translate any identifier into a Go name. +// Mostly used for fields, but used in a couple other places too (enum items). +func SrcName(p *Protocol, name string) string { + // If it's in the name map, use that translation. + if newn, ok := NameMap[name]; ok { + return newn + } + return splitAndTitle(name) +} + +func TypeSrcName(p *Protocol, typ Type) string { + t := typ.XmlName() + + // If this is a base type, then write the raw Go type. + if baseType, ok := typ.(*Base); ok { + return baseType.SrcName() + } + + // If it's in the type map, use that translation. + if newt, ok := TypeMap[t]; ok { + return newt + } + + // If there's a namespace to this type, just use it and be done. + if colon := strings.Index(t, ":"); colon > -1 { + namespace := t[:colon] + rest := t[colon+1:] + return p.ProtocolFind(namespace).PkgName() + "." + splitAndTitle(rest) + } + + // Since there's no namespace, we're left with the raw type name. + // If the type is part of the source we're generating (i.e., there is + // no parent protocol), then just return that type name. + // Otherwise, we must qualify it with a package name. + if p.Parent == nil { + return splitAndTitle(t) + } + return p.PkgName() + "." + splitAndTitle(t) +} |