func (id *TypeId) String() string {
	if id.ArrayDim == 0 && id.MapDim == 0 {
		return id.Tname
	} else if id.ArrayDim == 1 && id.MapDim == 0 {
		return id.Tname + "[]"
	}
	var buf strings.Builder
	buf.Grow(id.strlen())
	if id.MapDim > 0 {
		buf.WriteString(`map<`)
	}
	buf.WriteString(id.Tname)
	if id.MapDim > 0 {
		for i := int16(1); i < id.MapDim; i++ {
			buf.WriteString(`[]`)
		}
		buf.WriteRune('>')
	}
	for i := int16(0); i < id.ArrayDim; i++ {
		buf.WriteString(`[]`)
	}
	return buf.String()
}
String returns a string representation of the type ID in a human-readable
form.
package syntax
import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
)
type (
	FileKind int
	Type interface {
		fmt.Stringer
		TypeId() TypeId
		IsFile() FileKind
		ElementType() Type
		IsAssignableFrom(other Type, lookup *TypeLookup) error
		IsValidExpression(exp Exp, pipeline *Pipeline, ast *Ast) error
		CheckEqual(other Type) error
		CanFilter() bool
		IsValidJson(json.RawMessage, *strings.Builder, *TypeLookup) error
		FilterJson(json.RawMessage, *TypeLookup) (json.RawMessage, bool, error)
	}
	TypeId struct {
		Tname string
		ArrayDim int16
		MapDim int16
	}
	nullType struct{}
	IncompatibleTypeError struct {
		Message string
		Reason  error
	}
)
const (
	KindIsNotFile FileKind = iota
	KindMayContainPaths FileKind = iota
	KindIsFile FileKind = iota
	KindIsDirectory FileKind = iota
)
func (id TypeId) str() string {
	return id.String()
}
func (id *TypeId) writeTo(w stringWriter) {
	if id.MapDim > 0 {
		mustWriteString(w, `map<`)
	}
	mustWriteString(w, id.Tname)
	if id.MapDim > 0 {
		for i := int16(1); i < id.MapDim; i++ {
			mustWriteString(w, `[]`)
		}
		mustWriteRune(w, '>')
	}
	for i := int16(0); i < id.ArrayDim; i++ {
		mustWriteString(w, `[]`)
	}
}
func (id *TypeId) MarshalText() ([]byte, error) {
	if id.ArrayDim == 0 && id.MapDim == 0 {
		return []byte(id.Tname), nil
	}
	var buf bytes.Buffer
	buf.Grow(id.strlen())
	err := id.encodeText(&buf)
	return buf.Bytes(), err
}
func (id *TypeId) UnmarshalText(b []byte) error {
	var newId TypeId
	for len(b) > 2 && b[len(b)-1] == ']' && b[len(b)-2] == '[' {
		newId.ArrayDim++
		b = b[:len(b)-2]
	}
	if len(b) > 5 && b[len(b)-1] == '>' &&
		b[3] == '<' && b[2] == 'p' && b[1] == 'a' && b[0] == 'm' {
		newId.MapDim++
		b = b[:len(b)-1]
		b = b[4:]
		for len(b) > 2 && b[len(b)-1] == ']' && b[len(b)-2] == '[' {
			newId.MapDim++
			b = b[:len(b)-2]
		}
	}
	newId.Tname = string(b)
	*id = newId
	if strings.ContainsAny(newId.Tname, "[]<> ") {
		return fmt.Errorf("invalid type name %s", newId.Tname)
	}
	return nil
}
func (id TypeId) EncodeJSON(buf *bytes.Buffer) error {
	if _, err := buf.WriteRune('"'); err != nil {
		return err
	}
	if err := id.encodeText(buf); err != nil {
		return err
	}
	_, err := buf.WriteRune('"')
	return err
}
func (id *TypeId) encodeText(buf *bytes.Buffer) error {
	if id.MapDim > 0 {
		buf.WriteString(`map<`)
	}
	buf.WriteString(id.Tname)
	if id.MapDim > 0 {
		for i := int16(1); i < id.MapDim; i++ {
			buf.WriteString(`[]`)
		}
		buf.WriteRune('>')
	}
	for i := int16(0); i < id.ArrayDim; i++ {
		buf.WriteString(`[]`)
	}
	return nil
}
func (id TypeId) jsonSizeEstimate() int {
	return 2 + id.strlen()
}