...

Source file src/oss.terrastruct.com/d2/d2ast/d2ast.go

Documentation: oss.terrastruct.com/d2/d2ast

     1  // TODO: Remove boxes and cleanup like d2ir
     2  //
     3  // d2ast implements the d2 language's abstract syntax tree.
     4  //
     5  // Special characters to think about in parser:
     6  // #
     7  // """
     8  // ;
     9  // []
    10  // {}
    11  // |
    12  // $
    13  // '
    14  // "
    15  // \
    16  // :
    17  // .
    18  // --
    19  // <>
    20  // *
    21  // &
    22  // ()
    23  package d2ast
    24  
    25  import (
    26  	"bytes"
    27  	"encoding"
    28  	"errors"
    29  	"fmt"
    30  	"math/big"
    31  	"path"
    32  	"strconv"
    33  	"strings"
    34  	"unicode"
    35  	"unicode/utf16"
    36  	"unicode/utf8"
    37  
    38  	"oss.terrastruct.com/util-go/xdefer"
    39  )
    40  
    41  // Node is the base interface implemented by all d2 AST nodes.
    42  // TODO: add error node for autofmt of incomplete AST
    43  type Node interface {
    44  	node()
    45  
    46  	// Type returns the user friendly name of the node.
    47  	Type() string
    48  
    49  	// GetRange returns the range a node occupies in its file.
    50  	GetRange() Range
    51  
    52  	// TODO: add Children() for walking AST
    53  	// Children() []Node
    54  }
    55  
    56  var _ Node = &Comment{}
    57  var _ Node = &BlockComment{}
    58  
    59  var _ Node = &Null{}
    60  var _ Node = &Boolean{}
    61  var _ Node = &Number{}
    62  var _ Node = &UnquotedString{}
    63  var _ Node = &DoubleQuotedString{}
    64  var _ Node = &SingleQuotedString{}
    65  var _ Node = &BlockString{}
    66  var _ Node = &Substitution{}
    67  var _ Node = &Import{}
    68  
    69  var _ Node = &Array{}
    70  var _ Node = &Map{}
    71  var _ Node = &Key{}
    72  var _ Node = &KeyPath{}
    73  var _ Node = &Edge{}
    74  var _ Node = &EdgeIndex{}
    75  
    76  // Range represents a range between Start and End in Path.
    77  // It's also used in the d2parser package to represent the range of an error.
    78  //
    79  // note: See docs on Position.
    80  //
    81  // It has a custom JSON string encoding with encoding.TextMarshaler and
    82  // encoding.TextUnmarshaler to keep it compact as the JSON struct encoding is too verbose,
    83  // especially with json.MarshalIndent.
    84  //
    85  // It looks like path,start-end
    86  type Range struct {
    87  	Path  string
    88  	Start Position
    89  	End   Position
    90  }
    91  
    92  var _ fmt.Stringer = Range{}
    93  var _ encoding.TextMarshaler = Range{}
    94  var _ encoding.TextUnmarshaler = &Range{}
    95  
    96  func MakeRange(s string) Range {
    97  	var r Range
    98  	_ = r.UnmarshalText([]byte(s))
    99  	return r
   100  }
   101  
   102  // String returns a string representation of the range including only the path and start.
   103  //
   104  // If path is empty, it will be omitted.
   105  //
   106  // The format is path:start
   107  func (r Range) String() string {
   108  	var s strings.Builder
   109  	if r.Path != "" {
   110  		s.WriteString(r.Path)
   111  		s.WriteByte(':')
   112  	}
   113  
   114  	s.WriteString(r.Start.String())
   115  	return s.String()
   116  }
   117  
   118  // OneLine returns true if the Range starts and ends on the same line.
   119  func (r Range) OneLine() bool {
   120  	return r.Start.Line == r.End.Line
   121  }
   122  
   123  // See docs on Range.
   124  func (r Range) MarshalText() ([]byte, error) {
   125  	start, _ := r.Start.MarshalText()
   126  	end, _ := r.End.MarshalText()
   127  	return []byte(fmt.Sprintf("%s,%s-%s", r.Path, start, end)), nil
   128  }
   129  
   130  // See docs on Range.
   131  func (r *Range) UnmarshalText(b []byte) (err error) {
   132  	defer xdefer.Errorf(&err, "failed to unmarshal Range from %q", b)
   133  
   134  	i := bytes.LastIndexByte(b, '-')
   135  	if i == -1 {
   136  		return errors.New("missing End field")
   137  	}
   138  	end := b[i+1:]
   139  	b = b[:i]
   140  
   141  	i = bytes.LastIndexByte(b, ',')
   142  	if i == -1 {
   143  		return errors.New("missing Start field")
   144  	}
   145  	start := b[i+1:]
   146  	b = b[:i]
   147  
   148  	r.Path = string(b)
   149  	err = r.Start.UnmarshalText(start)
   150  	if err != nil {
   151  		return err
   152  	}
   153  	return r.End.UnmarshalText(end)
   154  }
   155  
   156  func (r Range) Before(r2 Range) bool {
   157  	return r.Start.Before(r2.Start)
   158  }
   159  
   160  // Position represents a line:column and byte position in a file.
   161  //
   162  // note: Line and Column are zero indexed.
   163  // note: Column and Byte are UTF-8 byte indexes unless byUTF16 was passed to Position.Advance in
   164  // .     which they are UTF-16 code unit indexes.
   165  // .     If intended for Javascript consumption like in the browser or via LSP, byUTF16 is
   166  // .     set to true.
   167  type Position struct {
   168  	Line   int
   169  	Column int
   170  	Byte   int
   171  }
   172  
   173  var _ fmt.Stringer = Position{}
   174  var _ encoding.TextMarshaler = Position{}
   175  var _ encoding.TextUnmarshaler = &Position{}
   176  
   177  // String returns a line:column representation of the position suitable for error messages.
   178  //
   179  // note: Should not normally be used directly, see Range.String()
   180  func (p Position) String() string {
   181  	return fmt.Sprintf("%d:%d", p.Line+1, p.Column+1)
   182  }
   183  
   184  func (p Position) Debug() string {
   185  	return fmt.Sprintf("%d:%d:%d", p.Line, p.Column, p.Byte)
   186  }
   187  
   188  // See docs on Range.
   189  func (p Position) MarshalText() ([]byte, error) {
   190  	return []byte(fmt.Sprintf("%d:%d:%d", p.Line, p.Column, p.Byte)), nil
   191  }
   192  
   193  // See docs on Range.
   194  func (p *Position) UnmarshalText(b []byte) (err error) {
   195  	defer xdefer.Errorf(&err, "failed to unmarshal Position from %q", b)
   196  
   197  	fields := bytes.Split(b, []byte{':'})
   198  	if len(fields) != 3 {
   199  		return errors.New("expected three fields")
   200  	}
   201  
   202  	p.Line, err = strconv.Atoi(string(fields[0]))
   203  	if err != nil {
   204  		return err
   205  	}
   206  	p.Column, err = strconv.Atoi(string(fields[1]))
   207  	if err != nil {
   208  		return err
   209  	}
   210  	p.Byte, err = strconv.Atoi(string(fields[2]))
   211  	return err
   212  }
   213  
   214  // From copies src into p. It's used in the d2parser package to set a node's Range.End to
   215  // the parser's current pos on all return paths with defer.
   216  func (p *Position) From(src *Position) {
   217  	*p = *src
   218  }
   219  
   220  // Advance advances p's Line, Column and Byte by r and returns
   221  // the new Position.
   222  // Set byUTF16 to advance the position as though r represents
   223  // a UTF-16 codepoint.
   224  func (p Position) Advance(r rune, byUTF16 bool) Position {
   225  	size := utf8.RuneLen(r)
   226  	if byUTF16 {
   227  		size = 1
   228  		r1, r2 := utf16.EncodeRune(r)
   229  		if r1 != '\uFFFD' && r2 != '\uFFFD' {
   230  			size = 2
   231  		}
   232  	}
   233  
   234  	if r == '\n' {
   235  		p.Line++
   236  		p.Column = 0
   237  	} else {
   238  		p.Column += size
   239  	}
   240  	p.Byte += size
   241  
   242  	return p
   243  }
   244  
   245  func (p Position) Subtract(r rune, byUTF16 bool) Position {
   246  	size := utf8.RuneLen(r)
   247  	if byUTF16 {
   248  		size = 1
   249  		r1, r2 := utf16.EncodeRune(r)
   250  		if r1 != '\uFFFD' && r2 != '\uFFFD' {
   251  			size = 2
   252  		}
   253  	}
   254  
   255  	if r == '\n' {
   256  		panic("d2ast: cannot subtract newline from Position")
   257  	} else {
   258  		p.Column -= size
   259  	}
   260  	p.Byte -= size
   261  
   262  	return p
   263  }
   264  
   265  func (p Position) AdvanceString(s string, byUTF16 bool) Position {
   266  	for _, r := range s {
   267  		p = p.Advance(r, byUTF16)
   268  	}
   269  	return p
   270  }
   271  
   272  func (p Position) SubtractString(s string, byUTF16 bool) Position {
   273  	for _, r := range s {
   274  		p = p.Subtract(r, byUTF16)
   275  	}
   276  	return p
   277  }
   278  
   279  func (p Position) Before(p2 Position) bool {
   280  	return p.Byte < p2.Byte
   281  }
   282  
   283  // MapNode is implemented by nodes that may be children of Maps.
   284  type MapNode interface {
   285  	Node
   286  	mapNode()
   287  }
   288  
   289  var _ MapNode = &Comment{}
   290  var _ MapNode = &BlockComment{}
   291  var _ MapNode = &Key{}
   292  var _ MapNode = &Substitution{}
   293  var _ MapNode = &Import{}
   294  
   295  // ArrayNode is implemented by nodes that may be children of Arrays.
   296  type ArrayNode interface {
   297  	Node
   298  	arrayNode()
   299  }
   300  
   301  // See Value for the rest.
   302  var _ ArrayNode = &Comment{}
   303  var _ ArrayNode = &BlockComment{}
   304  var _ ArrayNode = &Substitution{}
   305  var _ ArrayNode = &Import{}
   306  
   307  // Value is implemented by nodes that may be values of a key.
   308  type Value interface {
   309  	ArrayNode
   310  	value()
   311  }
   312  
   313  // See Scalar for rest.
   314  var _ Value = &Array{}
   315  var _ Value = &Map{}
   316  
   317  // Scalar is implemented by nodes that represent scalar values.
   318  type Scalar interface {
   319  	Value
   320  	scalar()
   321  	ScalarString() string
   322  }
   323  
   324  // See String for rest.
   325  var _ Scalar = &Null{}
   326  var _ Scalar = &Boolean{}
   327  var _ Scalar = &Number{}
   328  
   329  // String is implemented by nodes that represent strings.
   330  type String interface {
   331  	Scalar
   332  	SetString(string)
   333  	Copy() String
   334  	_string()
   335  }
   336  
   337  var _ String = &UnquotedString{}
   338  var _ String = &SingleQuotedString{}
   339  var _ String = &DoubleQuotedString{}
   340  var _ String = &BlockString{}
   341  
   342  func (c *Comment) node()            {}
   343  func (c *BlockComment) node()       {}
   344  func (n *Null) node()               {}
   345  func (b *Boolean) node()            {}
   346  func (n *Number) node()             {}
   347  func (s *UnquotedString) node()     {}
   348  func (s *DoubleQuotedString) node() {}
   349  func (s *SingleQuotedString) node() {}
   350  func (s *BlockString) node()        {}
   351  func (s *Substitution) node()       {}
   352  func (i *Import) node()             {}
   353  func (a *Array) node()              {}
   354  func (m *Map) node()                {}
   355  func (k *Key) node()                {}
   356  func (k *KeyPath) node()            {}
   357  func (e *Edge) node()               {}
   358  func (i *EdgeIndex) node()          {}
   359  
   360  func (c *Comment) Type() string            { return "comment" }
   361  func (c *BlockComment) Type() string       { return "block comment" }
   362  func (n *Null) Type() string               { return "null" }
   363  func (b *Boolean) Type() string            { return "boolean" }
   364  func (n *Number) Type() string             { return "number" }
   365  func (s *UnquotedString) Type() string     { return "unquoted string" }
   366  func (s *DoubleQuotedString) Type() string { return "double quoted string" }
   367  func (s *SingleQuotedString) Type() string { return "single quoted string" }
   368  func (s *BlockString) Type() string        { return s.Tag + " block string" }
   369  func (s *Substitution) Type() string       { return "substitution" }
   370  func (i *Import) Type() string             { return "import" }
   371  func (a *Array) Type() string              { return "array" }
   372  func (m *Map) Type() string                { return "map" }
   373  func (k *Key) Type() string                { return "map key" }
   374  func (k *KeyPath) Type() string            { return "key path" }
   375  func (e *Edge) Type() string               { return "edge" }
   376  func (i *EdgeIndex) Type() string          { return "edge index" }
   377  
   378  func (c *Comment) GetRange() Range            { return c.Range }
   379  func (c *BlockComment) GetRange() Range       { return c.Range }
   380  func (n *Null) GetRange() Range               { return n.Range }
   381  func (b *Boolean) GetRange() Range            { return b.Range }
   382  func (n *Number) GetRange() Range             { return n.Range }
   383  func (s *UnquotedString) GetRange() Range     { return s.Range }
   384  func (s *DoubleQuotedString) GetRange() Range { return s.Range }
   385  func (s *SingleQuotedString) GetRange() Range { return s.Range }
   386  func (s *BlockString) GetRange() Range        { return s.Range }
   387  func (s *Substitution) GetRange() Range       { return s.Range }
   388  func (i *Import) GetRange() Range             { return i.Range }
   389  func (a *Array) GetRange() Range              { return a.Range }
   390  func (m *Map) GetRange() Range                { return m.Range }
   391  func (k *Key) GetRange() Range                { return k.Range }
   392  func (k *KeyPath) GetRange() Range            { return k.Range }
   393  func (e *Edge) GetRange() Range               { return e.Range }
   394  func (i *EdgeIndex) GetRange() Range          { return i.Range }
   395  
   396  func (c *Comment) mapNode()      {}
   397  func (c *BlockComment) mapNode() {}
   398  func (k *Key) mapNode()          {}
   399  func (s *Substitution) mapNode() {}
   400  func (i *Import) mapNode()       {}
   401  
   402  func (c *Comment) arrayNode()            {}
   403  func (c *BlockComment) arrayNode()       {}
   404  func (n *Null) arrayNode()               {}
   405  func (b *Boolean) arrayNode()            {}
   406  func (n *Number) arrayNode()             {}
   407  func (s *UnquotedString) arrayNode()     {}
   408  func (s *DoubleQuotedString) arrayNode() {}
   409  func (s *SingleQuotedString) arrayNode() {}
   410  func (s *BlockString) arrayNode()        {}
   411  func (s *Substitution) arrayNode()       {}
   412  func (i *Import) arrayNode()             {}
   413  func (a *Array) arrayNode()              {}
   414  func (m *Map) arrayNode()                {}
   415  
   416  func (n *Null) value()               {}
   417  func (b *Boolean) value()            {}
   418  func (n *Number) value()             {}
   419  func (s *UnquotedString) value()     {}
   420  func (s *DoubleQuotedString) value() {}
   421  func (s *SingleQuotedString) value() {}
   422  func (s *BlockString) value()        {}
   423  func (a *Array) value()              {}
   424  func (m *Map) value()                {}
   425  func (i *Import) value()             {}
   426  
   427  func (n *Null) scalar()               {}
   428  func (b *Boolean) scalar()            {}
   429  func (n *Number) scalar()             {}
   430  func (s *UnquotedString) scalar()     {}
   431  func (s *DoubleQuotedString) scalar() {}
   432  func (s *SingleQuotedString) scalar() {}
   433  func (s *BlockString) scalar()        {}
   434  
   435  // TODO: mistake, move into parse.go
   436  func (n *Null) ScalarString() string    { return "" }
   437  func (b *Boolean) ScalarString() string { return strconv.FormatBool(b.Value) }
   438  func (n *Number) ScalarString() string  { return n.Raw }
   439  func (s *UnquotedString) ScalarString() string {
   440  	if len(s.Value) == 0 {
   441  		return ""
   442  	}
   443  	if s.Value[0].String == nil {
   444  		return ""
   445  	}
   446  	return *s.Value[0].String
   447  }
   448  func (s *DoubleQuotedString) ScalarString() string {
   449  	if len(s.Value) == 0 {
   450  		return ""
   451  	}
   452  	if s.Value[0].String == nil {
   453  		return ""
   454  	}
   455  	return *s.Value[0].String
   456  }
   457  func (s *SingleQuotedString) ScalarString() string { return s.Value }
   458  func (s *BlockString) ScalarString() string        { return s.Value }
   459  
   460  func (s *UnquotedString) SetString(s2 string)     { s.Value = []InterpolationBox{{String: &s2}} }
   461  func (s *DoubleQuotedString) SetString(s2 string) { s.Value = []InterpolationBox{{String: &s2}} }
   462  func (s *SingleQuotedString) SetString(s2 string) { s.Raw = ""; s.Value = s2 }
   463  func (s *BlockString) SetString(s2 string)        { s.Value = s2 }
   464  
   465  func (s *UnquotedString) Copy() String     { tmp := *s; return &tmp }
   466  func (s *DoubleQuotedString) Copy() String { tmp := *s; return &tmp }
   467  func (s *SingleQuotedString) Copy() String { tmp := *s; return &tmp }
   468  func (s *BlockString) Copy() String        { tmp := *s; return &tmp }
   469  
   470  func (s *UnquotedString) _string()     {}
   471  func (s *DoubleQuotedString) _string() {}
   472  func (s *SingleQuotedString) _string() {}
   473  func (s *BlockString) _string()        {}
   474  
   475  type Comment struct {
   476  	Range Range  `json:"range"`
   477  	Value string `json:"value"`
   478  }
   479  
   480  type BlockComment struct {
   481  	Range Range  `json:"range"`
   482  	Value string `json:"value"`
   483  }
   484  
   485  type Null struct {
   486  	Range Range `json:"range"`
   487  }
   488  
   489  type Boolean struct {
   490  	Range Range `json:"range"`
   491  	Value bool  `json:"value"`
   492  }
   493  
   494  type Number struct {
   495  	Range Range    `json:"range"`
   496  	Raw   string   `json:"raw"`
   497  	Value *big.Rat `json:"value"`
   498  }
   499  
   500  type UnquotedString struct {
   501  	Range Range              `json:"range"`
   502  	Value []InterpolationBox `json:"value"`
   503  	// Pattern holds the parsed glob pattern if in a key and the unquoted string represents a valid pattern.
   504  	Pattern []string `json:"pattern,omitempty"`
   505  }
   506  
   507  func (s *UnquotedString) Coalesce() {
   508  	var b strings.Builder
   509  	for _, box := range s.Value {
   510  		if box.String == nil {
   511  			break
   512  		}
   513  		b.WriteString(*box.String)
   514  	}
   515  	s.SetString(b.String())
   516  }
   517  
   518  func FlatUnquotedString(s string) *UnquotedString {
   519  	return &UnquotedString{
   520  		Value: []InterpolationBox{{String: &s}},
   521  	}
   522  }
   523  
   524  type DoubleQuotedString struct {
   525  	Range Range              `json:"range"`
   526  	Value []InterpolationBox `json:"value"`
   527  }
   528  
   529  func (s *DoubleQuotedString) Coalesce() {
   530  	var b strings.Builder
   531  	for _, box := range s.Value {
   532  		if box.String == nil {
   533  			break
   534  		}
   535  		b.WriteString(*box.String)
   536  	}
   537  	s.SetString(b.String())
   538  }
   539  
   540  func FlatDoubleQuotedString(s string) *DoubleQuotedString {
   541  	return &DoubleQuotedString{
   542  		Value: []InterpolationBox{{String: &s}},
   543  	}
   544  }
   545  
   546  type SingleQuotedString struct {
   547  	Range Range  `json:"range"`
   548  	Raw   string `json:"raw"`
   549  	Value string `json:"value"`
   550  }
   551  
   552  type BlockString struct {
   553  	Range Range `json:"range"`
   554  
   555  	// Quote contains the pipe delimiter for the block string.
   556  	// e.g. if 5 pipes were used to begin a block string, then Quote == "||||".
   557  	// The tag is not included.
   558  	Quote string `json:"quote"`
   559  	Tag   string `json:"tag"`
   560  	Value string `json:"value"`
   561  }
   562  
   563  type Array struct {
   564  	Range Range          `json:"range"`
   565  	Nodes []ArrayNodeBox `json:"nodes"`
   566  }
   567  
   568  type Map struct {
   569  	Range Range        `json:"range"`
   570  	Nodes []MapNodeBox `json:"nodes"`
   571  }
   572  
   573  func (m *Map) InsertAfter(cursor, n MapNode) {
   574  	afterIndex := len(m.Nodes) - 1
   575  
   576  	for i, n := range m.Nodes {
   577  		if n.Unbox() == cursor {
   578  			afterIndex = i
   579  		}
   580  	}
   581  
   582  	a := make([]MapNodeBox, 0, len(m.Nodes))
   583  	a = append(a, m.Nodes[:afterIndex+1]...)
   584  	a = append(a, MakeMapNodeBox(n))
   585  	a = append(a, m.Nodes[afterIndex+1:]...)
   586  	m.Nodes = a
   587  }
   588  
   589  func (m *Map) InsertBefore(cursor, n MapNode) {
   590  	beforeIndex := len(m.Nodes)
   591  
   592  	for i, n := range m.Nodes {
   593  		if n.Unbox() == cursor {
   594  			beforeIndex = i
   595  		}
   596  	}
   597  
   598  	a := make([]MapNodeBox, 0, len(m.Nodes))
   599  	a = append(a, m.Nodes[:beforeIndex]...)
   600  	a = append(a, MakeMapNodeBox(n))
   601  	a = append(a, m.Nodes[beforeIndex:]...)
   602  	m.Nodes = a
   603  }
   604  
   605  func (m *Map) IsFileMap() bool {
   606  	return m.Range.Start.Line == 0 && m.Range.Start.Column == 0
   607  }
   608  
   609  func (m *Map) HasFilter() bool {
   610  	for _, n := range m.Nodes {
   611  		if n.MapKey != nil && (n.MapKey.Ampersand || n.MapKey.NotAmpersand) {
   612  			return true
   613  		}
   614  	}
   615  	return false
   616  }
   617  
   618  // TODO: require @ on import values for readability
   619  type Key struct {
   620  	Range Range `json:"range"`
   621  
   622  	// Indicates this MapKey is a filter selector.
   623  	Ampersand bool `json:"ampersand,omitempty"`
   624  
   625  	// Indicates this MapKey is a not filter selector.
   626  	NotAmpersand bool `json:"not_ampersand,omitempty"`
   627  
   628  	// At least one of Key and Edges will be set but all four can also be set.
   629  	// The following are all valid MapKeys:
   630  	// Key:
   631  	//   x
   632  	// Edges:
   633  	//   x -> y
   634  	// Edges and EdgeIndex:
   635  	//   (x -> y)[*]
   636  	// Edges and EdgeKey:
   637  	//   (x -> y).label
   638  	// Key and Edges:
   639  	//   container.(x -> y)
   640  	// Key, Edges and EdgeKey:
   641  	//   container.(x -> y -> z).label
   642  	// Key, Edges, EdgeIndex EdgeKey:
   643  	//   container.(x -> y -> z)[4].label
   644  	Key       *KeyPath   `json:"key,omitempty"`
   645  	Edges     []*Edge    `json:"edges,omitempty"`
   646  	EdgeIndex *EdgeIndex `json:"edge_index,omitempty"`
   647  	EdgeKey   *KeyPath   `json:"edge_key,omitempty"`
   648  
   649  	Primary ScalarBox `json:"primary,omitempty"`
   650  	Value   ValueBox  `json:"value"`
   651  }
   652  
   653  func (mk1 *Key) D2OracleEquals(mk2 *Key) bool {
   654  	if mk1 == nil && mk2 == nil {
   655  		return true
   656  	}
   657  	if (mk1 == nil) || (mk2 == nil) {
   658  		return false
   659  	}
   660  	if mk1.Ampersand != mk2.Ampersand {
   661  		return false
   662  	}
   663  	if (mk1.Key == nil) != (mk2.Key == nil) {
   664  		return false
   665  	}
   666  	if (mk1.EdgeIndex == nil) != (mk2.EdgeIndex == nil) {
   667  		return false
   668  	}
   669  	if (mk1.EdgeKey == nil) != (mk2.EdgeKey == nil) {
   670  		return false
   671  	}
   672  	if len(mk1.Edges) != len(mk2.Edges) {
   673  		return false
   674  	}
   675  	if (mk1.Value.Map == nil) != (mk2.Value.Map == nil) {
   676  		if mk1.Value.Map != nil && len(mk1.Value.Map.Nodes) > 0 {
   677  			return false
   678  		}
   679  		if mk2.Value.Map != nil && len(mk2.Value.Map.Nodes) > 0 {
   680  			return false
   681  		}
   682  	} else if (mk1.Value.Unbox() == nil) != (mk2.Value.Unbox() == nil) {
   683  		return false
   684  	}
   685  
   686  	if mk1.Key != nil {
   687  		if len(mk1.Key.Path) != len(mk2.Key.Path) {
   688  			return false
   689  		}
   690  		for i, id := range mk1.Key.Path {
   691  			if id.Unbox().ScalarString() != mk2.Key.Path[i].Unbox().ScalarString() {
   692  				return false
   693  			}
   694  		}
   695  	}
   696  	if mk1.EdgeKey != nil {
   697  		if len(mk1.EdgeKey.Path) != len(mk2.EdgeKey.Path) {
   698  			return false
   699  		}
   700  		for i, id := range mk1.EdgeKey.Path {
   701  			if id.Unbox().ScalarString() != mk2.EdgeKey.Path[i].Unbox().ScalarString() {
   702  				return false
   703  			}
   704  		}
   705  	}
   706  
   707  	if mk1.Value.Map != nil && len(mk1.Value.Map.Nodes) > 0 {
   708  		if len(mk1.Value.Map.Nodes) != len(mk2.Value.Map.Nodes) {
   709  			return false
   710  		}
   711  		for i := range mk1.Value.Map.Nodes {
   712  			if !mk1.Value.Map.Nodes[i].MapKey.Equals(mk2.Value.Map.Nodes[i].MapKey) {
   713  				return false
   714  			}
   715  		}
   716  	}
   717  
   718  	if mk1.Value.Unbox() != nil {
   719  		if (mk1.Value.ScalarBox().Unbox() == nil) != (mk2.Value.ScalarBox().Unbox() == nil) {
   720  			return false
   721  		}
   722  		if mk1.Value.ScalarBox().Unbox() != nil {
   723  			if mk1.Value.ScalarBox().Unbox().ScalarString() != mk2.Value.ScalarBox().Unbox().ScalarString() {
   724  				return false
   725  			}
   726  		}
   727  	}
   728  
   729  	return true
   730  }
   731  
   732  func (mk1 *Key) Equals(mk2 *Key) bool {
   733  	if mk1 == nil && mk2 == nil {
   734  		return true
   735  	}
   736  	if (mk1 == nil) || (mk2 == nil) {
   737  		return false
   738  	}
   739  	if mk1.Ampersand != mk2.Ampersand {
   740  		return false
   741  	}
   742  	if (mk1.Key == nil) != (mk2.Key == nil) {
   743  		return false
   744  	}
   745  	if (mk1.EdgeIndex == nil) != (mk2.EdgeIndex == nil) {
   746  		return false
   747  	}
   748  	if mk1.EdgeIndex != nil {
   749  		if !mk1.EdgeIndex.Equals(mk2.EdgeIndex) {
   750  			return false
   751  		}
   752  	}
   753  	if (mk1.EdgeKey == nil) != (mk2.EdgeKey == nil) {
   754  		return false
   755  	}
   756  	if len(mk1.Edges) != len(mk2.Edges) {
   757  		return false
   758  	}
   759  	for i := range mk1.Edges {
   760  		if !mk1.Edges[i].Equals(mk2.Edges[i]) {
   761  			return false
   762  		}
   763  	}
   764  	if (mk1.Value.Map == nil) != (mk2.Value.Map == nil) {
   765  		if mk1.Value.Map != nil && len(mk1.Value.Map.Nodes) > 0 {
   766  			return false
   767  		}
   768  		if mk2.Value.Map != nil && len(mk2.Value.Map.Nodes) > 0 {
   769  			return false
   770  		}
   771  	} else if (mk1.Value.Unbox() == nil) != (mk2.Value.Unbox() == nil) {
   772  		return false
   773  	}
   774  
   775  	if mk1.Key != nil {
   776  		if len(mk1.Key.Path) != len(mk2.Key.Path) {
   777  			return false
   778  		}
   779  		for i, id := range mk1.Key.Path {
   780  			if id.Unbox().ScalarString() != mk2.Key.Path[i].Unbox().ScalarString() {
   781  				return false
   782  			}
   783  		}
   784  	}
   785  	if mk1.EdgeKey != nil {
   786  		if len(mk1.EdgeKey.Path) != len(mk2.EdgeKey.Path) {
   787  			return false
   788  		}
   789  		for i, id := range mk1.EdgeKey.Path {
   790  			if id.Unbox().ScalarString() != mk2.EdgeKey.Path[i].Unbox().ScalarString() {
   791  				return false
   792  			}
   793  		}
   794  	}
   795  
   796  	if mk1.Value.Map != nil && len(mk1.Value.Map.Nodes) > 0 {
   797  		if len(mk1.Value.Map.Nodes) != len(mk2.Value.Map.Nodes) {
   798  			return false
   799  		}
   800  		for i := range mk1.Value.Map.Nodes {
   801  			if !mk1.Value.Map.Nodes[i].MapKey.Equals(mk2.Value.Map.Nodes[i].MapKey) {
   802  				return false
   803  			}
   804  		}
   805  	}
   806  
   807  	if mk1.Value.Unbox() != nil {
   808  		if (mk1.Value.ScalarBox().Unbox() == nil) != (mk2.Value.ScalarBox().Unbox() == nil) {
   809  			return false
   810  		}
   811  		if mk1.Value.ScalarBox().Unbox() != nil {
   812  			if mk1.Value.ScalarBox().Unbox().ScalarString() != mk2.Value.ScalarBox().Unbox().ScalarString() {
   813  				return false
   814  			}
   815  		}
   816  	}
   817  
   818  	if mk1.Primary.Unbox() != nil {
   819  		if (mk1.Primary.Unbox() == nil) != (mk2.Primary.Unbox() == nil) {
   820  			return false
   821  		}
   822  		if mk1.Primary.ScalarString() != mk2.Primary.ScalarString() {
   823  			return false
   824  		}
   825  	}
   826  
   827  	return true
   828  }
   829  
   830  func (mk *Key) SetScalar(scalar ScalarBox) {
   831  	if mk.Value.Unbox() != nil && mk.Value.ScalarBox().Unbox() == nil {
   832  		mk.Primary = scalar
   833  	} else {
   834  		mk.Value = MakeValueBox(scalar.Unbox())
   835  	}
   836  }
   837  
   838  func (mk *Key) HasGlob() bool {
   839  	if mk.Key.HasGlob() {
   840  		return true
   841  	}
   842  	for _, e := range mk.Edges {
   843  		if e.Src.HasGlob() || e.Dst.HasGlob() {
   844  			return true
   845  		}
   846  	}
   847  	if mk.EdgeIndex != nil && mk.EdgeIndex.Glob {
   848  		return true
   849  	}
   850  	if mk.EdgeKey.HasGlob() {
   851  		return true
   852  	}
   853  	return false
   854  }
   855  
   856  func (mk *Key) HasTripleGlob() bool {
   857  	if mk.Key.HasTripleGlob() {
   858  		return true
   859  	}
   860  	for _, e := range mk.Edges {
   861  		if e.Src.HasTripleGlob() || e.Dst.HasTripleGlob() {
   862  			return true
   863  		}
   864  	}
   865  	if mk.EdgeIndex != nil && mk.EdgeIndex.Glob {
   866  		return true
   867  	}
   868  	if mk.EdgeKey.HasTripleGlob() {
   869  		return true
   870  	}
   871  	return false
   872  }
   873  
   874  func (mk *Key) SupportsGlobFilters() bool {
   875  	if mk.Key.HasGlob() && len(mk.Edges) == 0 {
   876  		return true
   877  	}
   878  	if mk.EdgeIndex != nil && mk.EdgeIndex.Glob && mk.EdgeKey == nil {
   879  		return true
   880  	}
   881  	if mk.EdgeKey.HasGlob() {
   882  		return true
   883  	}
   884  	return false
   885  }
   886  
   887  func (mk *Key) Copy() *Key {
   888  	mk2 := *mk
   889  	return &mk2
   890  }
   891  
   892  type KeyPath struct {
   893  	Range Range        `json:"range"`
   894  	Path  []*StringBox `json:"path"`
   895  }
   896  
   897  func MakeKeyPath(a []string) *KeyPath {
   898  	kp := &KeyPath{}
   899  	for _, el := range a {
   900  		kp.Path = append(kp.Path, MakeValueBox(RawString(el, true)).StringBox())
   901  	}
   902  	return kp
   903  }
   904  
   905  func (kp *KeyPath) IDA() (ida []string) {
   906  	for _, el := range kp.Path {
   907  		ida = append(ida, el.Unbox().ScalarString())
   908  	}
   909  	return ida
   910  }
   911  
   912  func (kp *KeyPath) Copy() *KeyPath {
   913  	kp2 := *kp
   914  	kp2.Path = nil
   915  	kp2.Path = append(kp2.Path, kp.Path...)
   916  	return &kp2
   917  }
   918  
   919  func (kp *KeyPath) Last() *StringBox {
   920  	return kp.Path[len(kp.Path)-1]
   921  }
   922  
   923  func IsDoubleGlob(pattern []string) bool {
   924  	return len(pattern) == 3 && pattern[0] == "*" && pattern[1] == "" && pattern[2] == "*"
   925  }
   926  
   927  func IsTripleGlob(pattern []string) bool {
   928  	return len(pattern) == 5 && pattern[0] == "*" && pattern[1] == "" && pattern[2] == "*" && pattern[3] == "" && pattern[4] == "*"
   929  }
   930  
   931  func (kp *KeyPath) HasGlob() bool {
   932  	if kp == nil {
   933  		return false
   934  	}
   935  	for _, el := range kp.Path {
   936  		if el.UnquotedString != nil && len(el.UnquotedString.Pattern) > 0 {
   937  			return true
   938  		}
   939  	}
   940  	return false
   941  }
   942  
   943  func (kp *KeyPath) FirstGlob() int {
   944  	if kp == nil {
   945  		return -1
   946  	}
   947  	for i, el := range kp.Path {
   948  		if el.UnquotedString != nil && len(el.UnquotedString.Pattern) > 0 {
   949  			return i
   950  		}
   951  	}
   952  	return -1
   953  }
   954  
   955  func (kp *KeyPath) HasTripleGlob() bool {
   956  	if kp == nil {
   957  		return false
   958  	}
   959  	for _, el := range kp.Path {
   960  		if el.UnquotedString != nil && IsTripleGlob(el.UnquotedString.Pattern) {
   961  			return true
   962  		}
   963  	}
   964  	return false
   965  }
   966  
   967  func (kp *KeyPath) HasMultiGlob() bool {
   968  	if kp == nil {
   969  		return false
   970  	}
   971  	for _, el := range kp.Path {
   972  		if el.UnquotedString != nil && (IsDoubleGlob(el.UnquotedString.Pattern) || IsTripleGlob(el.UnquotedString.Pattern)) {
   973  			return true
   974  		}
   975  	}
   976  	return false
   977  }
   978  
   979  func (kp1 *KeyPath) Equals(kp2 *KeyPath) bool {
   980  	if len(kp1.Path) != len(kp2.Path) {
   981  		return false
   982  	}
   983  	for i, id := range kp1.Path {
   984  		if id.Unbox().ScalarString() != kp2.Path[i].Unbox().ScalarString() {
   985  			return false
   986  		}
   987  	}
   988  	return true
   989  }
   990  
   991  type Edge struct {
   992  	Range Range `json:"range"`
   993  
   994  	Src *KeyPath `json:"src"`
   995  	// empty, < or *
   996  	SrcArrow string `json:"src_arrow"`
   997  
   998  	Dst *KeyPath `json:"dst"`
   999  	// empty, > or *
  1000  	DstArrow string `json:"dst_arrow"`
  1001  }
  1002  
  1003  func (e1 *Edge) Equals(e2 *Edge) bool {
  1004  	if !e1.Src.Equals(e2.Src) {
  1005  		return false
  1006  	}
  1007  	if e1.SrcArrow != e2.SrcArrow {
  1008  		return false
  1009  	}
  1010  	if !e1.Dst.Equals(e2.Dst) {
  1011  		return false
  1012  	}
  1013  	if e1.DstArrow != e2.DstArrow {
  1014  		return false
  1015  	}
  1016  	return true
  1017  }
  1018  
  1019  type EdgeIndex struct {
  1020  	Range Range `json:"range"`
  1021  
  1022  	// [n] or [*]
  1023  	Int  *int `json:"int"`
  1024  	Glob bool `json:"glob"`
  1025  }
  1026  
  1027  func (ei1 *EdgeIndex) Equals(ei2 *EdgeIndex) bool {
  1028  	if ei1.Int != ei2.Int {
  1029  		return false
  1030  	}
  1031  	if ei1.Glob != ei2.Glob {
  1032  		return false
  1033  	}
  1034  	return true
  1035  }
  1036  
  1037  type Substitution struct {
  1038  	Range Range `json:"range"`
  1039  
  1040  	Spread bool         `json:"spread"`
  1041  	Path   []*StringBox `json:"path"`
  1042  }
  1043  
  1044  type Import struct {
  1045  	Range Range `json:"range"`
  1046  
  1047  	Spread bool         `json:"spread"`
  1048  	Pre    string       `json:"pre"`
  1049  	Path   []*StringBox `json:"path"`
  1050  }
  1051  
  1052  // MapNodeBox is used to box MapNode for JSON persistence.
  1053  type MapNodeBox struct {
  1054  	Comment      *Comment      `json:"comment,omitempty"`
  1055  	BlockComment *BlockComment `json:"block_comment,omitempty"`
  1056  	Substitution *Substitution `json:"substitution,omitempty"`
  1057  	Import       *Import       `json:"import,omitempty"`
  1058  	MapKey       *Key          `json:"map_key,omitempty"`
  1059  }
  1060  
  1061  func MakeMapNodeBox(n MapNode) MapNodeBox {
  1062  	var box MapNodeBox
  1063  	switch n := n.(type) {
  1064  	case *Comment:
  1065  		box.Comment = n
  1066  	case *BlockComment:
  1067  		box.BlockComment = n
  1068  	case *Substitution:
  1069  		box.Substitution = n
  1070  	case *Import:
  1071  		box.Import = n
  1072  	case *Key:
  1073  		box.MapKey = n
  1074  	}
  1075  	return box
  1076  }
  1077  
  1078  func (mb MapNodeBox) Unbox() MapNode {
  1079  	switch {
  1080  	case mb.Comment != nil:
  1081  		return mb.Comment
  1082  	case mb.BlockComment != nil:
  1083  		return mb.BlockComment
  1084  	case mb.Substitution != nil:
  1085  		return mb.Substitution
  1086  	case mb.Import != nil:
  1087  		return mb.Import
  1088  	case mb.MapKey != nil:
  1089  		return mb.MapKey
  1090  	default:
  1091  		return nil
  1092  	}
  1093  }
  1094  
  1095  func (mb MapNodeBox) IsBoardNode() bool {
  1096  	if mb.MapKey == nil || mb.MapKey.Key == nil || len(mb.MapKey.Key.Path) != 1 {
  1097  		return false
  1098  	}
  1099  	switch mb.MapKey.Key.Path[0].Unbox().ScalarString() {
  1100  	case "layers", "scenarios", "steps":
  1101  		return true
  1102  	default:
  1103  		return false
  1104  	}
  1105  }
  1106  
  1107  // ArrayNodeBox is used to box ArrayNode for JSON persistence.
  1108  type ArrayNodeBox struct {
  1109  	Comment            *Comment            `json:"comment,omitempty"`
  1110  	BlockComment       *BlockComment       `json:"block_comment,omitempty"`
  1111  	Substitution       *Substitution       `json:"substitution,omitempty"`
  1112  	Import             *Import             `json:"import,omitempty"`
  1113  	Null               *Null               `json:"null,omitempty"`
  1114  	Boolean            *Boolean            `json:"boolean,omitempty"`
  1115  	Number             *Number             `json:"number,omitempty"`
  1116  	UnquotedString     *UnquotedString     `json:"unquoted_string,omitempty"`
  1117  	DoubleQuotedString *DoubleQuotedString `json:"double_quoted_string,omitempty"`
  1118  	SingleQuotedString *SingleQuotedString `json:"single_quoted_string,omitempty"`
  1119  	BlockString        *BlockString        `json:"block_string,omitempty"`
  1120  	Array              *Array              `json:"array,omitempty"`
  1121  	Map                *Map                `json:"map,omitempty"`
  1122  }
  1123  
  1124  func MakeArrayNodeBox(an ArrayNode) ArrayNodeBox {
  1125  	var ab ArrayNodeBox
  1126  	switch an := an.(type) {
  1127  	case *Comment:
  1128  		ab.Comment = an
  1129  	case *BlockComment:
  1130  		ab.BlockComment = an
  1131  	case *Substitution:
  1132  		ab.Substitution = an
  1133  	case *Import:
  1134  		ab.Import = an
  1135  	case *Null:
  1136  		ab.Null = an
  1137  	case *Boolean:
  1138  		ab.Boolean = an
  1139  	case *Number:
  1140  		ab.Number = an
  1141  	case *UnquotedString:
  1142  		ab.UnquotedString = an
  1143  	case *DoubleQuotedString:
  1144  		ab.DoubleQuotedString = an
  1145  	case *SingleQuotedString:
  1146  		ab.SingleQuotedString = an
  1147  	case *BlockString:
  1148  		ab.BlockString = an
  1149  	case *Array:
  1150  		ab.Array = an
  1151  	case *Map:
  1152  		ab.Map = an
  1153  	}
  1154  	return ab
  1155  }
  1156  
  1157  func (ab ArrayNodeBox) Unbox() ArrayNode {
  1158  	switch {
  1159  	case ab.Comment != nil:
  1160  		return ab.Comment
  1161  	case ab.BlockComment != nil:
  1162  		return ab.BlockComment
  1163  	case ab.Substitution != nil:
  1164  		return ab.Substitution
  1165  	case ab.Import != nil:
  1166  		return ab.Import
  1167  	case ab.Null != nil:
  1168  		return ab.Null
  1169  	case ab.Boolean != nil:
  1170  		return ab.Boolean
  1171  	case ab.Number != nil:
  1172  		return ab.Number
  1173  	case ab.UnquotedString != nil:
  1174  		return ab.UnquotedString
  1175  	case ab.DoubleQuotedString != nil:
  1176  		return ab.DoubleQuotedString
  1177  	case ab.SingleQuotedString != nil:
  1178  		return ab.SingleQuotedString
  1179  	case ab.BlockString != nil:
  1180  		return ab.BlockString
  1181  	case ab.Array != nil:
  1182  		return ab.Array
  1183  	case ab.Map != nil:
  1184  		return ab.Map
  1185  	default:
  1186  		return nil
  1187  	}
  1188  }
  1189  
  1190  // ValueBox is used to box Value for JSON persistence.
  1191  type ValueBox struct {
  1192  	Null               *Null               `json:"null,omitempty"`
  1193  	Boolean            *Boolean            `json:"boolean,omitempty"`
  1194  	Number             *Number             `json:"number,omitempty"`
  1195  	UnquotedString     *UnquotedString     `json:"unquoted_string,omitempty"`
  1196  	DoubleQuotedString *DoubleQuotedString `json:"double_quoted_string,omitempty"`
  1197  	SingleQuotedString *SingleQuotedString `json:"single_quoted_string,omitempty"`
  1198  	BlockString        *BlockString        `json:"block_string,omitempty"`
  1199  	Array              *Array              `json:"array,omitempty"`
  1200  	Map                *Map                `json:"map,omitempty"`
  1201  	Import             *Import             `json:"import,omitempty"`
  1202  }
  1203  
  1204  func (vb ValueBox) Unbox() Value {
  1205  	switch {
  1206  	case vb.Null != nil:
  1207  		return vb.Null
  1208  	case vb.Boolean != nil:
  1209  		return vb.Boolean
  1210  	case vb.Number != nil:
  1211  		return vb.Number
  1212  	case vb.UnquotedString != nil:
  1213  		return vb.UnquotedString
  1214  	case vb.DoubleQuotedString != nil:
  1215  		return vb.DoubleQuotedString
  1216  	case vb.SingleQuotedString != nil:
  1217  		return vb.SingleQuotedString
  1218  	case vb.BlockString != nil:
  1219  		return vb.BlockString
  1220  	case vb.Array != nil:
  1221  		return vb.Array
  1222  	case vb.Map != nil:
  1223  		return vb.Map
  1224  	case vb.Import != nil:
  1225  		return vb.Import
  1226  	default:
  1227  		return nil
  1228  	}
  1229  }
  1230  
  1231  func MakeValueBox(v Value) ValueBox {
  1232  	var vb ValueBox
  1233  	switch v := v.(type) {
  1234  	case *Null:
  1235  		vb.Null = v
  1236  	case *Boolean:
  1237  		vb.Boolean = v
  1238  	case *Number:
  1239  		vb.Number = v
  1240  	case *UnquotedString:
  1241  		vb.UnquotedString = v
  1242  	case *DoubleQuotedString:
  1243  		vb.DoubleQuotedString = v
  1244  	case *SingleQuotedString:
  1245  		vb.SingleQuotedString = v
  1246  	case *BlockString:
  1247  		vb.BlockString = v
  1248  	case *Array:
  1249  		vb.Array = v
  1250  	case *Map:
  1251  		vb.Map = v
  1252  	case *Import:
  1253  		vb.Import = v
  1254  	}
  1255  	return vb
  1256  }
  1257  
  1258  func (vb ValueBox) ScalarBox() ScalarBox {
  1259  	var sb ScalarBox
  1260  	sb.Null = vb.Null
  1261  	sb.Boolean = vb.Boolean
  1262  	sb.Number = vb.Number
  1263  	sb.UnquotedString = vb.UnquotedString
  1264  	sb.DoubleQuotedString = vb.DoubleQuotedString
  1265  	sb.SingleQuotedString = vb.SingleQuotedString
  1266  	sb.BlockString = vb.BlockString
  1267  	return sb
  1268  }
  1269  
  1270  func (vb ValueBox) StringBox() *StringBox {
  1271  	var sb StringBox
  1272  	sb.UnquotedString = vb.UnquotedString
  1273  	sb.DoubleQuotedString = vb.DoubleQuotedString
  1274  	sb.SingleQuotedString = vb.SingleQuotedString
  1275  	sb.BlockString = vb.BlockString
  1276  	return &sb
  1277  }
  1278  
  1279  // ScalarBox is used to box Scalar for JSON persistence.
  1280  // TODO: implement ScalarString()
  1281  type ScalarBox struct {
  1282  	Null               *Null               `json:"null,omitempty"`
  1283  	Boolean            *Boolean            `json:"boolean,omitempty"`
  1284  	Number             *Number             `json:"number,omitempty"`
  1285  	UnquotedString     *UnquotedString     `json:"unquoted_string,omitempty"`
  1286  	DoubleQuotedString *DoubleQuotedString `json:"double_quoted_string,omitempty"`
  1287  	SingleQuotedString *SingleQuotedString `json:"single_quoted_string,omitempty"`
  1288  	BlockString        *BlockString        `json:"block_string,omitempty"`
  1289  }
  1290  
  1291  func (sb ScalarBox) Unbox() Scalar {
  1292  	switch {
  1293  	case sb.Null != nil:
  1294  		return sb.Null
  1295  	case sb.Boolean != nil:
  1296  		return sb.Boolean
  1297  	case sb.Number != nil:
  1298  		return sb.Number
  1299  	case sb.UnquotedString != nil:
  1300  		return sb.UnquotedString
  1301  	case sb.DoubleQuotedString != nil:
  1302  		return sb.DoubleQuotedString
  1303  	case sb.SingleQuotedString != nil:
  1304  		return sb.SingleQuotedString
  1305  	case sb.BlockString != nil:
  1306  		return sb.BlockString
  1307  	default:
  1308  		return nil
  1309  	}
  1310  }
  1311  
  1312  func (sb ScalarBox) ScalarString() string {
  1313  	return sb.Unbox().ScalarString()
  1314  }
  1315  
  1316  // StringBox is used to box String for JSON persistence.
  1317  type StringBox struct {
  1318  	UnquotedString     *UnquotedString     `json:"unquoted_string,omitempty"`
  1319  	DoubleQuotedString *DoubleQuotedString `json:"double_quoted_string,omitempty"`
  1320  	SingleQuotedString *SingleQuotedString `json:"single_quoted_string,omitempty"`
  1321  	BlockString        *BlockString        `json:"block_string,omitempty"`
  1322  }
  1323  
  1324  func (sb *StringBox) Unbox() String {
  1325  	switch {
  1326  	case sb.UnquotedString != nil:
  1327  		return sb.UnquotedString
  1328  	case sb.DoubleQuotedString != nil:
  1329  		return sb.DoubleQuotedString
  1330  	case sb.SingleQuotedString != nil:
  1331  		return sb.SingleQuotedString
  1332  	case sb.BlockString != nil:
  1333  		return sb.BlockString
  1334  	default:
  1335  		return nil
  1336  	}
  1337  }
  1338  
  1339  func (sb *StringBox) ScalarString() string {
  1340  	return sb.Unbox().ScalarString()
  1341  }
  1342  
  1343  // InterpolationBox is used to select between strings and substitutions in unquoted and
  1344  // double quoted strings. There is no corresponding interface to avoid unnecessary
  1345  // abstraction.
  1346  type InterpolationBox struct {
  1347  	String       *string       `json:"string,omitempty"`
  1348  	StringRaw    *string       `json:"raw_string,omitempty"`
  1349  	Substitution *Substitution `json:"substitution,omitempty"`
  1350  }
  1351  
  1352  // & is only special if it begins a key.
  1353  // - is only special if followed by another - in a key.
  1354  // ' " and | are only special if they begin an unquoted key or value.
  1355  var UnquotedKeySpecials = string([]rune{'#', ';', '\n', '\\', '{', '}', '[', ']', '\'', '"', '|', ':', '.', '-', '<', '>', '*', '&', '(', ')', '@', '&'})
  1356  var UnquotedValueSpecials = string([]rune{'#', ';', '\n', '\\', '{', '}', '[', ']', '\'', '"', '|', '$', '@'})
  1357  
  1358  // RawString returns s in a AST String node that can format s in the most aesthetically
  1359  // pleasing way.
  1360  func RawString(s string, inKey bool) String {
  1361  	if s == "" {
  1362  		return FlatDoubleQuotedString(s)
  1363  	}
  1364  
  1365  	if inKey {
  1366  		for i, r := range s {
  1367  			switch r {
  1368  			case '-':
  1369  				if i+1 < len(s) && s[i+1] != '-' {
  1370  					continue
  1371  				}
  1372  			}
  1373  			if strings.ContainsRune(UnquotedKeySpecials, r) {
  1374  				if !strings.ContainsRune(s, '"') {
  1375  					return FlatDoubleQuotedString(s)
  1376  				}
  1377  				if strings.ContainsRune(s, '\n') {
  1378  					return FlatDoubleQuotedString(s)
  1379  				}
  1380  				return &SingleQuotedString{Value: s}
  1381  			}
  1382  		}
  1383  	} else if s == "null" || strings.ContainsAny(s, UnquotedValueSpecials) {
  1384  		if !strings.ContainsRune(s, '"') && !strings.ContainsRune(s, '$') {
  1385  			return FlatDoubleQuotedString(s)
  1386  		}
  1387  		if strings.ContainsRune(s, '\n') {
  1388  			return FlatDoubleQuotedString(s)
  1389  		}
  1390  		return &SingleQuotedString{Value: s}
  1391  	}
  1392  
  1393  	if hasSurroundingWhitespace(s) {
  1394  		return FlatDoubleQuotedString(s)
  1395  	}
  1396  
  1397  	return FlatUnquotedString(s)
  1398  }
  1399  
  1400  func RawStringBox(s string, inKey bool) *StringBox {
  1401  	return MakeValueBox(RawString(s, inKey)).StringBox()
  1402  }
  1403  
  1404  func hasSurroundingWhitespace(s string) bool {
  1405  	r, _ := utf8.DecodeRuneInString(s)
  1406  	r2, _ := utf8.DecodeLastRuneInString(s)
  1407  	return unicode.IsSpace(r) || unicode.IsSpace(r2)
  1408  }
  1409  
  1410  func (s *Substitution) IDA() (ida []string) {
  1411  	for _, el := range s.Path {
  1412  		ida = append(ida, el.Unbox().ScalarString())
  1413  	}
  1414  	return ida
  1415  }
  1416  
  1417  func (i *Import) IDA() (ida []string) {
  1418  	for _, el := range i.Path[1:] {
  1419  		ida = append(ida, el.Unbox().ScalarString())
  1420  	}
  1421  	return ida
  1422  }
  1423  
  1424  func (i *Import) PathWithPre() string {
  1425  	if len(i.Path) == 0 {
  1426  		return ""
  1427  	}
  1428  	return path.Join(i.Pre, i.Path[0].Unbox().ScalarString())
  1429  }
  1430  

View as plain text