| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| /* Package datafmt implements syntax-directed, type-driven formatting |
| of arbitrary data structures. Formatting a data structure consists of |
| two phases: first, a parser reads a format specification and builds a |
| "compiled" format. Then, the format can be applied repeatedly to |
| arbitrary values. Applying a format to a value evaluates to a []byte |
| containing the formatted value bytes, or nil. |
| |
| A format specification is a set of package declarations and format rules: |
| |
| Format = [ Entry { ";" Entry } [ ";" ] ] . |
| Entry = PackageDecl | FormatRule . |
| |
| (The syntax of a format specification is presented in the same EBNF |
| notation as used in the Go language specification. The syntax of white |
| space, comments, identifiers, and string literals is the same as in Go.) |
| |
| A package declaration binds a package name (such as 'ast') to a |
| package import path (such as '"go/ast"'). Each package used (in |
| a type name, see below) must be declared once before use. |
| |
| PackageDecl = PackageName ImportPath . |
| PackageName = identifier . |
| ImportPath = string . |
| |
| A format rule binds a rule name to a format expression. A rule name |
| may be a type name or one of the special names 'default' or '/'. |
| A type name may be the name of a predeclared type (for example, 'int', |
| 'float32', etc.), the package-qualified name of a user-defined type |
| (for example, 'ast.MapType'), or an identifier indicating the structure |
| of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', |
| or 'ptr'). Each rule must have a unique name; rules can be declared in |
| any order. |
| |
| FormatRule = RuleName "=" Expression . |
| RuleName = TypeName | "default" | "/" . |
| TypeName = [ PackageName "." ] identifier . |
| |
| To format a value, the value's type name is used to select the format rule |
| (there is an override mechanism, see below). The format expression of the |
| selected rule specifies how the value is formatted. Each format expression, |
| when applied to a value, evaluates to a byte sequence or nil. |
| |
| In its most general form, a format expression is a list of alternatives, |
| each of which is a sequence of operands: |
| |
| Expression = [ Sequence ] { "|" [ Sequence ] } . |
| Sequence = Operand { Operand } . |
| |
| The formatted result produced by an expression is the result of the first |
| alternative sequence that evaluates to a non-nil result; if there is no |
| such alternative, the expression evaluates to nil. The result produced by |
| an operand sequence is the concatenation of the results of its operands. |
| If any operand in the sequence evaluates to nil, the entire sequence |
| evaluates to nil. |
| |
| There are five kinds of operands: |
| |
| Operand = Literal | Field | Group | Option | Repetition . |
| |
| Literals evaluate to themselves, with two substitutions. First, |
| %-formats expand in the manner of fmt.Printf, with the current value |
| passed as the parameter. Second, the current indentation (see below) |
| is inserted after every newline or form feed character. |
| |
| Literal = string . |
| |
| This table shows string literals applied to the value 42 and the |
| corresponding formatted result: |
| |
| "foo" foo |
| "%x" 2a |
| "x = %d" x = 42 |
| "%#x = %d" 0x2a = 42 |
| |
| A field operand is a field name optionally followed by an alternate |
| rule name. The field name may be an identifier or one of the special |
| names @ or *. |
| |
| Field = FieldName [ ":" RuleName ] . |
| FieldName = identifier | "@" | "*" . |
| |
| If the field name is an identifier, the current value must be a struct, |
| and there must be a field with that name in the struct. The same lookup |
| rules apply as in the Go language (for instance, the name of an anonymous |
| field is the unqualified type name). The field name denotes the field |
| value in the struct. If the field is not found, formatting is aborted |
| and an error message is returned. (TODO consider changing the semantics |
| such that if a field is not found, it evaluates to nil). |
| |
| The special name '@' denotes the current value. |
| |
| The meaning of the special name '*' depends on the type of the current |
| value: |
| |
| array, slice types array, slice element (inside {} only, see below) |
| interfaces value stored in interface |
| pointers value pointed to by pointer |
| |
| (Implementation restriction: channel, function and map types are not |
| supported due to missing reflection support). |
| |
| Fields are evaluated as follows: If the field value is nil, or an array |
| or slice element does not exist, the result is nil (see below for details |
| on array/slice elements). If the value is not nil the field value is |
| formatted (recursively) using the rule corresponding to its type name, |
| or the alternate rule name, if given. |
| |
| The following example shows a complete format specification for a |
| struct 'myPackage.Point'. Assume the package |
| |
| package myPackage // in directory myDir/myPackage |
| type Point struct { |
| name string; |
| x, y int; |
| } |
| |
| Applying the format specification |
| |
| myPackage "myDir/myPackage"; |
| int = "%d"; |
| hexInt = "0x%x"; |
| string = "---%s---"; |
| myPackage.Point = name "{" x ", " y:hexInt "}"; |
| |
| to the value myPackage.Point{"foo", 3, 15} results in |
| |
| ---foo---{3, 0xf} |
| |
| Finally, an operand may be a grouped, optional, or repeated expression. |
| A grouped expression ("group") groups a more complex expression (body) |
| so that it can be used in place of a single operand: |
| |
| Group = "(" [ Indentation ">>" ] Body ")" . |
| Indentation = Expression . |
| Body = Expression . |
| |
| A group body may be prefixed by an indentation expression followed by '>>'. |
| The indentation expression is applied to the current value like any other |
| expression and the result, if not nil, is appended to the current indentation |
| during the evaluation of the body (see also formatting state, below). |
| |
| An optional expression ("option") is enclosed in '[]' brackets. |
| |
| Option = "[" Body "]" . |
| |
| An option evaluates to its body, except that if the body evaluates to nil, |
| the option expression evaluates to an empty []byte. Thus an option's purpose |
| is to protect the expression containing the option from a nil operand. |
| |
| A repeated expression ("repetition") is enclosed in '{}' braces. |
| |
| Repetition = "{" Body [ "/" Separator ] "}" . |
| Separator = Expression . |
| |
| A repeated expression is evaluated as follows: The body is evaluated |
| repeatedly and its results are concatenated until the body evaluates |
| to nil. The result of the repetition is the (possibly empty) concatenation, |
| but it is never nil. An implicit index is supplied for the evaluation of |
| the body: that index is used to address elements of arrays or slices. If |
| the corresponding elements do not exist, the field denoting the element |
| evaluates to nil (which in turn may terminate the repetition). |
| |
| The body of a repetition may be followed by a '/' and a "separator" |
| expression. If the separator is present, it is invoked between repetitions |
| of the body. |
| |
| The following example shows a complete format specification for formatting |
| a slice of unnamed type. Applying the specification |
| |
| int = "%b"; |
| array = { * / ", " }; // array is the type name for an unnamed slice |
| |
| to the value '[]int{2, 3, 5, 7}' results in |
| |
| 10, 11, 101, 111 |
| |
| Default rule: If a format rule named 'default' is present, it is used for |
| formatting a value if no other rule was found. A common default rule is |
| |
| default = "%v" |
| |
| to provide default formatting for basic types without having to specify |
| a specific rule for each basic type. |
| |
| Global separator rule: If a format rule named '/' is present, it is |
| invoked with the current value between literals. If the separator |
| expression evaluates to nil, it is ignored. |
| |
| For instance, a global separator rule may be used to punctuate a sequence |
| of values with commas. The rules: |
| |
| default = "%v"; |
| / = ", "; |
| |
| will format an argument list by printing each one in its default format, |
| separated by a comma and a space. |
| */ |
| package datafmt |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/token" |
| "io" |
| "os" |
| "reflect" |
| "runtime" |
| ) |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Format representation |
| |
| // Custom formatters implement the Formatter function type. |
| // A formatter is invoked with the current formatting state, the |
| // value to format, and the rule name under which the formatter |
| // was installed (the same formatter function may be installed |
| // under different names). The formatter may access the current state |
| // to guide formatting and use State.Write to append to the state's |
| // output. |
| // |
| // A formatter must return a boolean value indicating if it evaluated |
| // to a non-nil value (true), or a nil value (false). |
| // |
| type Formatter func(state *State, value interface{}, ruleName string) bool |
| |
| |
| // A FormatterMap is a set of custom formatters. |
| // It maps a rule name to a formatter function. |
| // |
| type FormatterMap map[string]Formatter |
| |
| |
| // A parsed format expression is built from the following nodes. |
| // |
| type ( |
| expr interface{} |
| |
| alternatives []expr // x | y | z |
| |
| sequence []expr // x y z |
| |
| literal [][]byte // a list of string segments, possibly starting with '%' |
| |
| field struct { |
| fieldName string // including "@", "*" |
| ruleName string // "" if no rule name specified |
| } |
| |
| group struct { |
| indent, body expr // (indent >> body) |
| } |
| |
| option struct { |
| body expr // [body] |
| } |
| |
| repetition struct { |
| body, separator expr // {body / separator} |
| } |
| |
| custom struct { |
| ruleName string |
| fun Formatter |
| } |
| ) |
| |
| |
| // A Format is the result of parsing a format specification. |
| // The format may be applied repeatedly to format values. |
| // |
| type Format map[string]expr |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Formatting |
| |
| // An application-specific environment may be provided to Format.Apply; |
| // the environment is available inside custom formatters via State.Env(). |
| // Environments must implement copying; the Copy method must return an |
| // complete copy of the receiver. This is necessary so that the formatter |
| // can save and restore an environment (in case of an absent expression). |
| // |
| // If the Environment doesn't change during formatting (this is under |
| // control of the custom formatters), the Copy function can simply return |
| // the receiver, and thus can be very light-weight. |
| // |
| type Environment interface { |
| Copy() Environment |
| } |
| |
| |
| // State represents the current formatting state. |
| // It is provided as argument to custom formatters. |
| // |
| type State struct { |
| fmt Format // format in use |
| env Environment // user-supplied environment |
| errors chan os.Error // not chan *Error (errors <- nil would be wrong!) |
| hasOutput bool // true after the first literal has been written |
| indent bytes.Buffer // current indentation |
| output bytes.Buffer // format output |
| linePos token.Position // position of line beginning (Column == 0) |
| default_ expr // possibly nil |
| separator expr // possibly nil |
| } |
| |
| |
| func newState(fmt Format, env Environment, errors chan os.Error) *State { |
| s := new(State) |
| s.fmt = fmt |
| s.env = env |
| s.errors = errors |
| s.linePos = token.Position{Line: 1} |
| |
| // if we have a default rule, cache it's expression for fast access |
| if x, found := fmt["default"]; found { |
| s.default_ = x |
| } |
| |
| // if we have a global separator rule, cache it's expression for fast access |
| if x, found := fmt["/"]; found { |
| s.separator = x |
| } |
| |
| return s |
| } |
| |
| |
| // Env returns the environment passed to Format.Apply. |
| func (s *State) Env() interface{} { return s.env } |
| |
| |
| // LinePos returns the position of the current line beginning |
| // in the state's output buffer. Line numbers start at 1. |
| // |
| func (s *State) LinePos() token.Position { return s.linePos } |
| |
| |
| // Pos returns the position of the next byte to be written to the |
| // output buffer. Line numbers start at 1. |
| // |
| func (s *State) Pos() token.Position { |
| offs := s.output.Len() |
| return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs} |
| } |
| |
| |
| // Write writes data to the output buffer, inserting the indentation |
| // string after each newline or form feed character. It cannot return an error. |
| // |
| func (s *State) Write(data []byte) (int, os.Error) { |
| n := 0 |
| i0 := 0 |
| for i, ch := range data { |
| if ch == '\n' || ch == '\f' { |
| // write text segment and indentation |
| n1, _ := s.output.Write(data[i0 : i+1]) |
| n2, _ := s.output.Write(s.indent.Bytes()) |
| n += n1 + n2 |
| i0 = i + 1 |
| s.linePos.Offset = s.output.Len() |
| s.linePos.Line++ |
| } |
| } |
| n3, _ := s.output.Write(data[i0:]) |
| return n + n3, nil |
| } |
| |
| |
| type checkpoint struct { |
| env Environment |
| hasOutput bool |
| outputLen int |
| linePos token.Position |
| } |
| |
| |
| func (s *State) save() checkpoint { |
| saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos} |
| if s.env != nil { |
| saved.env = s.env.Copy() |
| } |
| return saved |
| } |
| |
| |
| func (s *State) restore(m checkpoint) { |
| s.env = m.env |
| s.output.Truncate(m.outputLen) |
| } |
| |
| |
| func (s *State) error(msg string) { |
| s.errors <- os.NewError(msg) |
| runtime.Goexit() |
| } |
| |
| |
| // TODO At the moment, unnamed types are simply mapped to the default |
| // names below. For instance, all unnamed arrays are mapped to |
| // 'array' which is not really sufficient. Eventually one may want |
| // to be able to specify rules for say an unnamed slice of T. |
| // |
| |
| func typename(typ reflect.Type) string { |
| switch typ.Kind() { |
| case reflect.Array: |
| return "array" |
| case reflect.Slice: |
| return "array" |
| case reflect.Chan: |
| return "chan" |
| case reflect.Func: |
| return "func" |
| case reflect.Interface: |
| return "interface" |
| case reflect.Map: |
| return "map" |
| case reflect.Ptr: |
| return "ptr" |
| } |
| return typ.String() |
| } |
| |
| func (s *State) getFormat(name string) expr { |
| if fexpr, found := s.fmt[name]; found { |
| return fexpr |
| } |
| |
| if s.default_ != nil { |
| return s.default_ |
| } |
| |
| s.error(fmt.Sprintf("no format rule for type: '%s'", name)) |
| return nil |
| } |
| |
| |
| // eval applies a format expression fexpr to a value. If the expression |
| // evaluates internally to a non-nil []byte, that slice is appended to |
| // the state's output buffer and eval returns true. Otherwise, eval |
| // returns false and the state remains unchanged. |
| // |
| func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { |
| // an empty format expression always evaluates |
| // to a non-nil (but empty) []byte |
| if fexpr == nil { |
| return true |
| } |
| |
| switch t := fexpr.(type) { |
| case alternatives: |
| // append the result of the first alternative that evaluates to |
| // a non-nil []byte to the state's output |
| mark := s.save() |
| for _, x := range t { |
| if s.eval(x, value, index) { |
| return true |
| } |
| s.restore(mark) |
| } |
| return false |
| |
| case sequence: |
| // append the result of all operands to the state's output |
| // unless a nil result is encountered |
| mark := s.save() |
| for _, x := range t { |
| if !s.eval(x, value, index) { |
| s.restore(mark) |
| return false |
| } |
| } |
| return true |
| |
| case literal: |
| // write separator, if any |
| if s.hasOutput { |
| // not the first literal |
| if s.separator != nil { |
| sep := s.separator // save current separator |
| s.separator = nil // and disable it (avoid recursion) |
| mark := s.save() |
| if !s.eval(sep, value, index) { |
| s.restore(mark) |
| } |
| s.separator = sep // enable it again |
| } |
| } |
| s.hasOutput = true |
| // write literal segments |
| for _, lit := range t { |
| if len(lit) > 1 && lit[0] == '%' { |
| // segment contains a %-format at the beginning |
| if lit[1] == '%' { |
| // "%%" is printed as a single "%" |
| s.Write(lit[1:]) |
| } else { |
| // use s instead of s.output to get indentation right |
| fmt.Fprintf(s, string(lit), value.Interface()) |
| } |
| } else { |
| // segment contains no %-formats |
| s.Write(lit) |
| } |
| } |
| return true // a literal never evaluates to nil |
| |
| case *field: |
| // determine field value |
| switch t.fieldName { |
| case "@": |
| // field value is current value |
| |
| case "*": |
| // indirection: operation is type-specific |
| switch v := value; v.Kind() { |
| case reflect.Array: |
| if v.Len() <= index { |
| return false |
| } |
| value = v.Index(index) |
| |
| case reflect.Slice: |
| if v.IsNil() || v.Len() <= index { |
| return false |
| } |
| value = v.Index(index) |
| |
| case reflect.Map: |
| s.error("reflection support for maps incomplete") |
| |
| case reflect.Ptr: |
| if v.IsNil() { |
| return false |
| } |
| value = v.Elem() |
| |
| case reflect.Interface: |
| if v.IsNil() { |
| return false |
| } |
| value = v.Elem() |
| |
| case reflect.Chan: |
| s.error("reflection support for chans incomplete") |
| |
| case reflect.Func: |
| s.error("reflection support for funcs incomplete") |
| |
| default: |
| s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type())) |
| } |
| |
| default: |
| // value is value of named field |
| var field reflect.Value |
| if sval := value; sval.Kind() == reflect.Struct { |
| field = sval.FieldByName(t.fieldName) |
| if !field.IsValid() { |
| // TODO consider just returning false in this case |
| s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type())) |
| } |
| } |
| value = field |
| } |
| |
| // determine rule |
| ruleName := t.ruleName |
| if ruleName == "" { |
| // no alternate rule name, value type determines rule |
| ruleName = typename(value.Type()) |
| } |
| fexpr = s.getFormat(ruleName) |
| |
| mark := s.save() |
| if !s.eval(fexpr, value, index) { |
| s.restore(mark) |
| return false |
| } |
| return true |
| |
| case *group: |
| // remember current indentation |
| indentLen := s.indent.Len() |
| |
| // update current indentation |
| mark := s.save() |
| s.eval(t.indent, value, index) |
| // if the indentation evaluates to nil, the state's output buffer |
| // didn't change - either way it's ok to append the difference to |
| // the current indentation |
| s.indent.Write(s.output.Bytes()[mark.outputLen:s.output.Len()]) |
| s.restore(mark) |
| |
| // format group body |
| mark = s.save() |
| b := true |
| if !s.eval(t.body, value, index) { |
| s.restore(mark) |
| b = false |
| } |
| |
| // reset indentation |
| s.indent.Truncate(indentLen) |
| return b |
| |
| case *option: |
| // evaluate the body and append the result to the state's output |
| // buffer unless the result is nil |
| mark := s.save() |
| if !s.eval(t.body, value, 0) { // TODO is 0 index correct? |
| s.restore(mark) |
| } |
| return true // an option never evaluates to nil |
| |
| case *repetition: |
| // evaluate the body and append the result to the state's output |
| // buffer until a result is nil |
| for i := 0; ; i++ { |
| mark := s.save() |
| // write separator, if any |
| if i > 0 && t.separator != nil { |
| // nil result from separator is ignored |
| mark := s.save() |
| if !s.eval(t.separator, value, i) { |
| s.restore(mark) |
| } |
| } |
| if !s.eval(t.body, value, i) { |
| s.restore(mark) |
| break |
| } |
| } |
| return true // a repetition never evaluates to nil |
| |
| case *custom: |
| // invoke the custom formatter to obtain the result |
| mark := s.save() |
| if !t.fun(s, value.Interface(), t.ruleName) { |
| s.restore(mark) |
| return false |
| } |
| return true |
| } |
| |
| panic("unreachable") |
| return false |
| } |
| |
| |
| // Eval formats each argument according to the format |
| // f and returns the resulting []byte and os.Error. If |
| // an error occurred, the []byte contains the partially |
| // formatted result. An environment env may be passed |
| // in which is available in custom formatters through |
| // the state parameter. |
| // |
| func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error) { |
| if f == nil { |
| return nil, os.NewError("format is nil") |
| } |
| |
| errors := make(chan os.Error) |
| s := newState(f, env, errors) |
| |
| go func() { |
| for _, v := range args { |
| fld := reflect.ValueOf(v) |
| if !fld.IsValid() { |
| errors <- os.NewError("nil argument") |
| return |
| } |
| mark := s.save() |
| if !s.eval(s.getFormat(typename(fld.Type())), fld, 0) { // TODO is 0 index correct? |
| s.restore(mark) |
| } |
| } |
| errors <- nil // no errors |
| }() |
| |
| err := <-errors |
| return s.output.Bytes(), err |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Convenience functions |
| |
| // Fprint formats each argument according to the format f |
| // and writes to w. The result is the total number of bytes |
| // written and an os.Error, if any. |
| // |
| func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error) { |
| data, err := f.Eval(env, args...) |
| if err != nil { |
| // TODO should we print partial result in case of error? |
| return 0, err |
| } |
| return w.Write(data) |
| } |
| |
| |
| // Print formats each argument according to the format f |
| // and writes to standard output. The result is the total |
| // number of bytes written and an os.Error, if any. |
| // |
| func (f Format) Print(args ...interface{}) (int, os.Error) { |
| return f.Fprint(os.Stdout, nil, args...) |
| } |
| |
| |
| // Sprint formats each argument according to the format f |
| // and returns the resulting string. If an error occurs |
| // during formatting, the result string contains the |
| // partially formatted result followed by an error message. |
| // |
| func (f Format) Sprint(args ...interface{}) string { |
| var buf bytes.Buffer |
| _, err := f.Fprint(&buf, nil, args...) |
| if err != nil { |
| var i interface{} = args |
| fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(i), err) |
| } |
| return buf.String() |
| } |