diff options
author | Andrew Gallant (Ocelot) <Andrew.Gallant@tufts.edu> | 2012-04-29 03:38:29 -0400 |
---|---|---|
committer | Andrew Gallant (Ocelot) <Andrew.Gallant@tufts.edu> | 2012-04-29 03:38:29 -0400 |
commit | 6bf0191fb01f4c0b65bcd444bb5381013c627f95 (patch) | |
tree | f83d00c55a92568e8345849c6753253d17bc393d /nexgb | |
parent | 52a21b415ad95b2c4649254447388cb329cee1a4 (diff) | |
download | haven-6bf0191fb01f4c0b65bcd444bb5381013c627f95.tar.gz haven-6bf0191fb01f4c0b65bcd444bb5381013c627f95.tar.xz haven-6bf0191fb01f4c0b65bcd444bb5381013c627f95.zip |
progress. still not working. this is incredibly difficult.
Diffstat (limited to 'nexgb')
-rw-r--r-- | nexgb/.gitignore | 2 | ||||
-rw-r--r-- | nexgb/xgb.go | 22 | ||||
-rw-r--r-- | nexgb/xgb_help.go | 32 | ||||
-rw-r--r-- | nexgb/xgbgen/context.go | 36 | ||||
-rw-r--r-- | nexgb/xgbgen/go.go | 291 | ||||
-rw-r--r-- | nexgb/xgbgen/main.go | 1 | ||||
-rw-r--r-- | nexgb/xgbgen/misc.go | 9 | ||||
-rwxr-xr-x | nexgb/xgbgen/xgbgen | bin | 2318165 -> 0 bytes | |||
-rw-r--r-- | nexgb/xgbgen/xml.go | 56 | ||||
-rw-r--r-- | nexgb/xgbgen/xml_fields.go | 14 |
10 files changed, 384 insertions, 79 deletions
diff --git a/nexgb/.gitignore b/nexgb/.gitignore new file mode 100644 index 0000000..1c0248d --- /dev/null +++ b/nexgb/.gitignore @@ -0,0 +1,2 @@ +xgbgen +.*.swp diff --git a/nexgb/xgb.go b/nexgb/xgb.go index 7e209a7..ce2cc54 100644 --- a/nexgb/xgb.go +++ b/nexgb/xgb.go @@ -87,9 +87,15 @@ func newCookie(id uint16) *Cookie { } } -// Event is an interface that can contain any of the events returned by the server. -// Use a type assertion switch to extract the Event structs. -type Event interface{} +// Event is an interface that can contain any of the events returned by the +// server. Use a type assertion switch to extract the Event structs. +type Event interface { + ImplementsEvent() +} + +// newEventFuncs is a map from event numbers to functions that create +// the corresponding event. +var newEventFuncs map[int]func(buf []byte) Event // Error contains protocol errors returned to us by the X server. type Error struct { @@ -100,6 +106,16 @@ type Error struct { Id Id } +// Error2 is an interface that can contain any of the errors returned by +// the server. Use a type assertion switch to extract the Error structs. +type Error2 interface { + ImplementsError() +} + +// newErrorFuncs is a map from error numbers to functions that create +// the corresponding error. +var newErrorFuncs map[int]func(buf []byte) Error2 + func (e *Error) Error() string { return fmt.Sprintf("Bad%s (major=%d minor=%d cookie=%d id=0x%x)", errorNames[e.Detail], e.Major, e.Minor, e.Cookie, e.Id) diff --git a/nexgb/xgb_help.go b/nexgb/xgb_help.go index adb97e0..acb35de 100644 --- a/nexgb/xgb_help.go +++ b/nexgb/xgb_help.go @@ -85,19 +85,19 @@ func (c *Conn) DefaultScreen() *ScreenInfo { return &c.Setup.Roots[c.defaultScre // ClientMessageData holds the data from a client message, // duplicated in three forms because Go doesn't have unions. -type ClientMessageData struct { - Data8 [20]byte - Data16 [10]uint16 - Data32 [5]uint32 -} - -func getClientMessageData(b []byte, v *ClientMessageData) int { - copy(v.Data8[:], b) - for i := 0; i < 10; i++ { - v.Data16[i] = get16(b[i*2:]) - } - for i := 0; i < 5; i++ { - v.Data32[i] = get32(b[i*4:]) - } - return 20 -} +// type ClientMessageData struct { + // Data8 [20]byte + // Data16 [10]uint16 + // Data32 [5]uint32 +// } +// +// func getClientMessageData(b []byte, v *ClientMessageData) int { + // copy(v.Data8[:], b) + // for i := 0; i < 10; i++ { + // v.Data16[i] = get16(b[i*2:]) + // } + // for i := 0; i < 5; i++ { + // v.Data32[i] = get32(b[i*4:]) + // } + // return 20 +// } diff --git a/nexgb/xgbgen/context.go b/nexgb/xgbgen/context.go index e5acb12..712cad4 100644 --- a/nexgb/xgbgen/context.go +++ b/nexgb/xgbgen/context.go @@ -5,7 +5,6 @@ import ( "encoding/xml" "fmt" "log" - "strings" ) type Context struct { @@ -33,31 +32,6 @@ func (c *Context) Put(format string, v ...interface{}) { } } -// TypePrefix searches the parsed XML for a type matching 'needle'. -// It then returns the appropriate prefix to be used in source code. -// Note that the core X protocol *is* a namespace, but does not have a prefix. -// Also note that you should probably check the BaseTypeMap and TypeMap -// before calling this function. -func (c *Context) TypePrefix(needle Type) string { - // If this is xproto, quit. No prefixes needed. - if c.xml.Header == "xproto" { - return "" - } - - // First check for the type in the current namespace. - if c.xml.HasType(needle) { - return strings.Title(c.xml.Header) - } - - // Now check each of the imports... - for _, imp := range c.xml.Imports { - if imp.xml.Header != "xproto" && imp.xml.HasType(needle) { - return strings.Title(imp.xml.Header) - } - } - - return "" -} // Translate is the big daddy of them all. It takes in an XML byte slice // and writes Go code to the 'out' buffer. @@ -76,14 +50,4 @@ func (c *Context) Translate(xmlBytes []byte) { // It's Morphin' Time! c.xml.Morph(c) - - // for _, req := range c.xml.Requests { - // if req.Name != "CreateContext" && req.Name != "MakeCurrent" { - // continue - // } - // log.Println(req.Name) - // for _, field := range req.Fields { - // log.Println("\t", field.XMLName.Local, field.Type.Morph(c)) - // } - // } } diff --git a/nexgb/xgbgen/go.go b/nexgb/xgbgen/go.go index eb3f0fb..bfc54dd 100644 --- a/nexgb/xgbgen/go.go +++ b/nexgb/xgbgen/go.go @@ -8,12 +8,22 @@ package main * Imports and helper variables. * Manual type and name override maps. + * Constants for tweaking various morphing functions. * Helper morphing functions. + * Morphing functions for each "sub-unit." * Morphing functions for each "unit". * Morphing functions for collections of "units". + + Units can be thought of as the top-level elements in an XML protocol + description file. Namely, structs, xidtypes, imports, enums, unions, etc. + Collections of units are simply "all of the UNIT in the XML file." + Sub-units can be thought of as recurring bits like struct contents (which + is used in events, replies, requests, errors, etc.) and expression + evaluation. */ import ( + "log" "strings" ) @@ -38,6 +48,24 @@ var BaseTypeMap = map[string]string{ "BOOL": "bool", "float": "float64", "double": "float64", + "char": "byte", +} + +// BaseTypeSizes should have precisely the same keys as in BaseTypeMap, +// and the values should correspond to the size of the type in bytes. +var BaseTypeSizes = map[string]uint{ + "CARD8": 1, + "CARD16": 2, + "CARD32": 4, + "INT8": 1, + "INT16": 2, + "INT32": 4, + "BYTE": 1, + "BOOL": 1, + "float": 4, + "double": 8, + "char": 1, + "Id": 4, } // TypeMap is a map from types in the XML to type names that is used @@ -54,12 +82,26 @@ var TypeMap = map[string]string{ var NameMap = map[string]string{ } /******************************************************************************/ -// Helper functions that aide in morphing repetive constructs. -// i.e., "structure contents", expressions, type and identifier names, etc. +// Constants for changing the semantics of morphing functions. +// These are mainly used to tweaking the writing of fields. +// Namely, reading/writing is not exactly the same across events, +// requests/replies and errors. +/******************************************************************************/ +const ( + FieldsEvent = iota + FieldsRequestReply + FieldsError +) + +/******************************************************************************/ +// Helper functions that aide in morphing repetitive constructs. +// i.e., type and identifier names, etc. /******************************************************************************/ // Morph changes every TYPE (not names) into something suitable -// for your language. +// for your language. It also handles adding suffixes like 'Event' +// and 'Union'. (A 'Union' suffix is used in Go because unions aren't +// supported at the language level.) func (typ Type) Morph(c *Context) string { t := string(typ) @@ -87,7 +129,33 @@ func (typ Type) Morph(c *Context) string { // Since there is no namespace, we need to look for a namespace // in the current context. - return c.TypePrefix(typ) + splitAndTitle(t) + return typ.Prefix(c) + splitAndTitle(t) +} + +// Prefix searches the parsed XML for a type matching 'typ'. +// It then returns the appropriate prefix to be used in source code. +// Note that the core X protocol *is* a namespace, but does not have a prefix. +// Also note that you should probably check the BaseTypeMap and TypeMap +// before calling this function. +func (typ Type) Prefix(c *Context) string { + // If this is xproto, quit. No prefixes needed. + if c.xml.Header == "xproto" { + return "" + } + + // First check for the type in the current namespace. + if c.xml.HasType(typ) { + return strings.Title(c.xml.Header) + } + + // Now check each of the imports... + for _, imp := range c.xml.Imports { + if imp.xml.Header != "xproto" && imp.xml.HasType(typ) { + return strings.Title(imp.xml.Header) + } + } + + return "" } // Morph changes every identifier (NOT type) into something suitable @@ -104,6 +172,147 @@ func (name Name) Morph(c *Context) string { } /******************************************************************************/ +// Sub-unit morphing. +// Below are functions that morph sub-units. Like collections of fields, +// expressions, etc. +// Note that collections of fields can be used in three different contexts: +// definitions, reading from the wire and writing to the wire. Thus, there +// exists 'MorphDefine', 'MorphRead', 'MorphWrite' defined on Fields. +/******************************************************************************/ +func (fields Fields) MorphDefine(c *Context) { + for _, field := range fields { + field.MorphDefine(c) + } +} + +func (field *Field) MorphDefine(c *Context) { + // We omit 'pad' and 'exprfield' + switch field.XMLName.Local { + case "field": + c.Putln("%s %s", field.Name.Morph(c), field.Type.Morph(c)) + case "list": + c.Putln("%s []%s", field.Name.Morph(c), field.Type.Morph(c)) + case "localfield": + c.Putln("%s %s", field.Name.Morph(c), field.Type.Morph(c)) + case "valueparam": + c.Putln("%s %s", field.ValueMaskName.Morph(c), + field.ValueMaskType.Morph(c)) + c.Putln("%s []%s", field.ValueListName.Morph(c), + field.ValueMaskType.Morph(c)) + case "switch": + field.Bitcases.MorphDefine(c) + } +} + +func (bitcases Bitcases) MorphDefine(c *Context) { + for _, bitcase := range bitcases { + bitcase.MorphDefine(c) + } +} + +func (bitcase *Bitcase) MorphDefine(c *Context) { + bitcase.Fields.MorphDefine(c) +} + +func (fields Fields) MorphRead(c *Context, kind int, evNoSeq bool) { + var nextByte uint + + switch kind { + case FieldsEvent: + nextByte = 1 + } + + for _, field := range fields { + nextByte = field.MorphRead(c, kind, nextByte) + switch kind { + case FieldsEvent: + // Skip the sequence id + if !evNoSeq && (nextByte == 2 || nextByte == 3) { + nextByte = 4 + } + } + } +} + +func (field *Field) MorphRead(c *Context, kind int, byt uint) uint { + consumed := uint(0) + switch field.XMLName.Local { + case "pad": + consumed = uint(field.Bytes) + case "field": + if field.Type == "ClientMessageData" { + break + } + size := field.Type.Size(c) + typ := field.Type.Morph(c) + name := field.Name.Morph(c) + _, isBase := BaseTypeMap[string(field.Type)] + + c.Put("v.%s = ", name) + if !isBase { + c.Put("%s(", typ) + } + switch size { + case 1: c.Put("buf[%d]", byt) + case 2: c.Put("get16(buf[%d:])", byt) + case 4: c.Put("get32(buf[%d:])", byt) + case 8: c.Put("get64(buf[%d:])", byt) + default: + log.Fatalf("Unsupported field size '%d' for field '%s'.", + size, field) + } + if !isBase { + c.Put(")") + } + c.Putln("") + + consumed = size + case "list": + c.Putln("") + } + return byt + consumed +} + +func (fields Fields) MorphWrite(c *Context, kind int) { + var nextByte uint + + switch kind { + case FieldsEvent: + nextByte = 1 + } + + for _, field := range fields { + nextByte = field.MorphWrite(c, kind, nextByte) + } +} + +func (field *Field) MorphWrite(c *Context, kind int, byt uint) uint { + consumed := uint(0) + switch field.XMLName.Local { + case "pad": + consumed = uint(field.Bytes) + case "field": + size := field.Type.Size(c) + typ := field.Type.Morph(c) + name := field.Name.Morph(c) + switch size { + case 1: + c.Putln("v.%s = %s(buf[%d])", name, typ, byt) + case 2: + c.Putln("v.%s = %s(get16(buf[%d:]))", name, typ, byt) + case 4: + c.Putln("v.%s = %s(get32(buf[%d:]))", name, typ, byt) + case 8: + c.Putln("v.%s = %s(get64(buf[%d:]))", name, typ, byt) + } + consumed = size + case "list": + c.Putln("IDK") + } + return byt + consumed +} + +/******************************************************************************/ // Per element morphing. // Below are functions that morph a single unit. /******************************************************************************/ @@ -133,15 +342,23 @@ func (xid *Xid) Morph(c *Context) { // TypeDef morphing. func (typedef *TypeDef) Morph(c *Context) { - c.Putln("type %s %s", typedef.Old.Morph(c), typedef.New.Morph(c)) + c.Putln("type %s %s", typedef.New.Morph(c), typedef.Old.Morph(c)) } // Struct morphing. func (strct *Struct) Morph(c *Context) { + c.Putln("type %s struct {", strct.Name.Morph(c)) + strct.Fields.MorphDefine(c) + c.Putln("}") + c.Putln("\n") } // Union morphing. func (union *Union) Morph(c *Context) { + c.Putln("type %s struct {", union.Name.Morph(c)) + union.Fields.MorphDefine(c) + c.Putln("}") + c.Putln("\n") } // Request morphing. @@ -150,10 +367,54 @@ func (request *Request) Morph(c *Context) { // Event morphing. func (ev *Event) Morph(c *Context) { + name := ev.Name.Morph(c) + + c.Putln("const %s = %d", name, ev.Number) + c.Putln("") + c.Putln("type %sEvent struct {", name) + ev.Fields.MorphDefine(c) + c.Putln("}") + c.Putln("") + c.Putln("func New%s(buf []byte) %sEvent {", name, name) + c.Putln("var v %sEvent", name) + ev.Fields.MorphRead(c, FieldsEvent, ev.NoSequence) + c.Putln("return v") + c.Putln("}") + c.Putln("") + c.Putln("func (err %sEvent) ImplementsEvent() { }", name) + c.Putln("") + c.Putln("func (ev %sEvent) Bytes() []byte {", name) + // ev.Fields.MorphWrite(c, FieldsEvent) + c.Putln("}") + c.Putln("") + c.Putln("func init() {") + c.Putln("newEventFuncs[%d] = New%s", ev.Number, name) + c.Putln("}") + c.Putln("") } // EventCopy morphing. func (evcopy *EventCopy) Morph(c *Context) { + oldName, newName := evcopy.Ref.Morph(c), evcopy.Name.Morph(c) + + c.Putln("const %s = %d", newName, evcopy.Number) + c.Putln("") + c.Putln("type %sEvent %sEvent", newName, oldName) + c.Putln("") + c.Putln("func New%s(buf []byte) %sEvent {", newName, newName) + c.Putln("return (%sEvent)(New%s(buf))", newName, oldName) + c.Putln("}") + c.Putln("") + c.Putln("func (err %sEvent) ImplementsEvent() { }", newName) + c.Putln("") + c.Putln("func (ev %sEvent) Bytes() []byte {", newName) + c.Putln("return (%sEvent)(ev).Bytes()", oldName) + c.Putln("}") + c.Putln("") + c.Putln("func init() {") + c.Putln("newEventFuncs[%d] = New%s", evcopy.Number, newName) + c.Putln("}") + c.Putln("") } // Error morphing. @@ -162,6 +423,26 @@ func (err *Error) Morph(c *Context) { // ErrorCopy morphing. func (errcopy *ErrorCopy) Morph(c *Context) { + oldName, newName := errcopy.Ref.Morph(c), errcopy.Name.Morph(c) + + c.Putln("const Bad%s = %d", newName, errcopy.Number) + c.Putln("") + c.Putln("type %sError %sError", newName, oldName) + c.Putln("") + c.Putln("func New%sError(buf []byte) %sError {", newName, newName) + c.Putln("return (%sError)(New%sError(buf))", newName, oldName) + c.Putln("}") + c.Putln("") + c.Putln("func (err %sError) ImplementsError() { }", newName) + c.Putln("") + c.Putln("func (err %sError) Bytes() []byte {", newName) + c.Putln("return (%sError)(err).Bytes()", oldName) + c.Putln("}") + c.Putln("") + c.Putln("func init() {") + c.Putln("newErrorFuncs[%d] = New%sError", errcopy.Number, newName) + c.Putln("}") + c.Putln("") } /******************************************************************************/ diff --git a/nexgb/xgbgen/main.go b/nexgb/xgbgen/main.go index 69579a4..c69c8aa 100644 --- a/nexgb/xgbgen/main.go +++ b/nexgb/xgbgen/main.go @@ -55,6 +55,7 @@ func main() { cmdGofmt := exec.Command("gofmt") cmdGofmt.Stdin = c.out cmdGofmt.Stdout = os.Stdout + cmdGofmt.Stderr = os.Stderr err = cmdGofmt.Run() if err != nil { log.Fatal(err) diff --git a/nexgb/xgbgen/misc.go b/nexgb/xgbgen/misc.go index 9adcf5d..13c4cc2 100644 --- a/nexgb/xgbgen/misc.go +++ b/nexgb/xgbgen/misc.go @@ -21,6 +21,11 @@ func popCount(mask uint) uint { return n } +// pad makes sure 'n' aligns on 4 bytes. +func pad(n int) int { + return (n + 3) & ^3 +} + // splitAndTitle takes a string, splits it by underscores, capitalizes the // first letter of each chunk, and smushes'em back together. func splitAndTitle(s string) string { @@ -29,9 +34,9 @@ func splitAndTitle(s string) string { return strings.Title(strings.ToLower(s)) } - // If the string has no underscores, leave it be. + // If the string has no underscores, capitalize it and leave it be. if i := strings.Index(s, "_"); i == -1 { - return s + return strings.Title(s) } // Now split the name at underscores, capitalize the first diff --git a/nexgb/xgbgen/xgbgen b/nexgb/xgbgen/xgbgen Binary files differdeleted file mode 100755 index ef33abc..0000000 --- a/nexgb/xgbgen/xgbgen +++ /dev/null diff --git a/nexgb/xgbgen/xml.go b/nexgb/xgbgen/xml.go index 0f632b4..12932b2 100644 --- a/nexgb/xgbgen/xml.go +++ b/nexgb/xgbgen/xml.go @@ -71,16 +71,16 @@ func (x *XML) Morph(c *Context) { x.Requests.Morph(c) c.Putln("") - x.Events.Morph(c) + x.Errors.Morph(c) c.Putln("") - x.Errors.Morph(c) + x.ErrorCopies.Morph(c) c.Putln("") - x.EventCopies.Morph(c) + x.Events.Morph(c) c.Putln("") - x.ErrorCopies.Morph(c) + x.EventCopies.Morph(c) c.Putln("") } @@ -166,6 +166,38 @@ type Name string type Type string +// Size is a nifty function that takes any type and digs until it finds +// its underlying base type. At which point, the size can be determined. +func (typ Type) Size(c *Context) uint { + // If this is a base type, we're done. + if size, ok := BaseTypeSizes[string(typ)]; ok { + return size + } + + // If it's a resource, we're also done. + if c.xml.IsResource(typ) { + return BaseTypeSizes["Id"] + } + + // It's not, so that implies there is *some* typedef declaring it + // in terms of another type. Just follow that chain until we get to + // a base type. We also need to check imported stuff. + for _, typedef := range c.xml.TypeDefs { + if typ == typedef.New { + return typedef.Old.Size(c) + } + } + for _, imp := range c.xml.Imports { + for _, typedef := range imp.xml.TypeDefs { + if typ == typedef.New { + return typedef.Old.Size(c) + } + } + } + log.Panicf("Could not find base size of type '%s'.", typ) + panic("unreachable") +} + type Imports []*Import func (imports Imports) Eval() { @@ -239,7 +271,7 @@ type EventCopies []*EventCopy type EventCopy struct { Name Type `xml:"name,attr"` - Number string `xml:"number,attr"` + Number int `xml:"number,attr"` Ref Type `xml:"ref,attr"` } @@ -247,7 +279,7 @@ type ErrorCopies []*ErrorCopy type ErrorCopy struct { Name Type `xml:"name,attr"` - Number string `xml:"number,attr"` + Number int `xml:"number,attr"` Ref Type `xml:"ref,attr"` } @@ -255,14 +287,14 @@ type Structs []*Struct type Struct struct { Name Type `xml:"name,attr"` - Fields []*Field `xml:",any"` + Fields Fields `xml:",any"` } type Unions []*Union type Union struct { Name Type `xml:"name,attr"` - Fields []*Field `xml:",any"` + Fields Fields `xml:",any"` } type Requests []*Request @@ -271,12 +303,12 @@ type Request struct { Name Type `xml:"name,attr"` Opcode int `xml:"opcode,attr"` Combine bool `xml:"combine-adjacent,attr"` - Fields []*Field `xml:",any"` + Fields Fields `xml:",any"` Reply *Reply `xml:"reply"` } type Reply struct { - Fields []*Field `xml:",any"` + Fields Fields `xml:",any"` } type Events []*Event @@ -285,7 +317,7 @@ type Event struct { Name Type `xml:"name,attr"` Number int `xml:"number,attr"` NoSequence bool `xml:"no-sequence-number,true"` - Fields []*Field `xml:",any"` + Fields Fields `xml:",any"` } type Errors []*Error @@ -293,6 +325,6 @@ type Errors []*Error type Error struct { Name Type `xml:"name,attr"` Number int `xml:"number,attr"` - Fields []*Field `xml:",any"` + Fields Fields `xml:",any"` } diff --git a/nexgb/xgbgen/xml_fields.go b/nexgb/xgbgen/xml_fields.go index 18be6e3..c1a7240 100644 --- a/nexgb/xgbgen/xml_fields.go +++ b/nexgb/xgbgen/xml_fields.go @@ -23,6 +23,8 @@ import ( "strings" ) +type Fields []*Field + type Field struct { XMLName xml.Name @@ -30,7 +32,7 @@ type Field struct { Bytes int `xml:"bytes,attr"` // For 'field', 'list', 'localfield', 'exprfield' and 'switch' elements. - Name string `xml:"name,attr"` + Name Name `xml:"name,attr"` // For 'field', 'list', 'localfield', and 'exprfield' elements. Type Type `xml:"type,attr"` @@ -40,11 +42,11 @@ type Field struct { // For 'valueparm' element. ValueMaskType Type `xml:"value-mask-type,attr"` - ValueMaskName string `xml:"value-mask-name,attr"` - ValueListName string `xml:"value-list-name,attr"` + ValueMaskName Name `xml:"value-mask-name,attr"` + ValueListName Name `xml:"value-list-name,attr"` // For 'switch' element. - Bitcases []*Bitcase `xml:"bitcase"` + Bitcases Bitcases `xml:"bitcase"` // I don't know which elements these are for. The documentation is vague. // They also seem to be completely optional. @@ -86,6 +88,8 @@ func (f *Field) String() string { panic("unreachable") } +type Bitcases []*Bitcase + // 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, @@ -95,7 +99,7 @@ func (f *Field) String() string { // 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 Bitcase struct { - Fields []*Field `xml:",any"` + Fields Fields `xml:",any"` // All the different expressions. // When it comes time to choose one, use the 'Expr' method. |