github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/blueprint/parser/ast.go (about)

     1  // Copyright 2016 Google Inc. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package parser
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"text/scanner"
    21  )
    22  
    23  type Node interface {
    24  	// Pos returns the position of the first token in the Node
    25  	Pos() scanner.Position
    26  	// End returns the position of the character after the last token in the Node
    27  	End() scanner.Position
    28  }
    29  
    30  // Definition is an Assignment or a Module at the top level of a Blueprints file
    31  type Definition interface {
    32  	Node
    33  	String() string
    34  	definitionTag()
    35  }
    36  
    37  // An Assignment is a variable assignment at the top level of a Blueprints file, scoped to the
    38  // file and and subdirs.
    39  type Assignment struct {
    40  	Name       string
    41  	NamePos    scanner.Position
    42  	Value      Expression
    43  	OrigValue  Expression
    44  	EqualsPos  scanner.Position
    45  	Assigner   string
    46  	Referenced bool
    47  }
    48  
    49  func (a *Assignment) String() string {
    50  	return fmt.Sprintf("%s@%s %s %s (%s) %t", a.Name, a.EqualsPos, a.Assigner, a.Value, a.OrigValue, a.Referenced)
    51  }
    52  
    53  func (a *Assignment) Pos() scanner.Position { return a.NamePos }
    54  func (a *Assignment) End() scanner.Position { return a.Value.End() }
    55  
    56  func (a *Assignment) definitionTag() {}
    57  
    58  // A Module is a module definition at the top level of a Blueprints file
    59  type Module struct {
    60  	Type    string
    61  	TypePos scanner.Position
    62  	Map
    63  }
    64  
    65  func (m *Module) Copy() *Module {
    66  	ret := *m
    67  	ret.Properties = make([]*Property, len(m.Properties))
    68  	for i := range m.Properties {
    69  		ret.Properties[i] = m.Properties[i].Copy()
    70  	}
    71  	return &ret
    72  }
    73  
    74  func (m *Module) String() string {
    75  	propertyStrings := make([]string, len(m.Properties))
    76  	for i, property := range m.Properties {
    77  		propertyStrings[i] = property.String()
    78  	}
    79  	return fmt.Sprintf("%s@%s-%s{%s}", m.Type,
    80  		m.LBracePos, m.RBracePos,
    81  		strings.Join(propertyStrings, ", "))
    82  }
    83  
    84  func (m *Module) definitionTag() {}
    85  
    86  func (m *Module) Pos() scanner.Position { return m.TypePos }
    87  func (m *Module) End() scanner.Position { return m.Map.End() }
    88  
    89  // A Property is a name: value pair within a Map, which may be a top level Module.
    90  type Property struct {
    91  	Name     string
    92  	NamePos  scanner.Position
    93  	ColonPos scanner.Position
    94  	Value    Expression
    95  }
    96  
    97  func (p *Property) Copy() *Property {
    98  	ret := *p
    99  	ret.Value = p.Value.Copy()
   100  	return &ret
   101  }
   102  
   103  func (p *Property) String() string {
   104  	return fmt.Sprintf("%s@%s: %s", p.Name, p.ColonPos, p.Value)
   105  }
   106  
   107  func (p *Property) Pos() scanner.Position { return p.NamePos }
   108  func (p *Property) End() scanner.Position { return p.Value.End() }
   109  
   110  // An Expression is a Value in a Property or Assignment.  It can be a literal (String or Bool), a
   111  // Map, a List, an Operator that combines two expressions of the same type, or a Variable that
   112  // references and Assignment.
   113  type Expression interface {
   114  	Node
   115  	// Copy returns a copy of the Expression that will not affect the original if mutated
   116  	Copy() Expression
   117  	String() string
   118  	// Type returns the underlying Type enum of the Expression if it were to be evalutated
   119  	Type() Type
   120  	// Eval returns an expression that is fully evaluated to a simple type (List, Map, String, or
   121  	// Bool).  It will return the same object for every call to Eval().
   122  	Eval() Expression
   123  }
   124  
   125  // ExpressionsAreSame tells whether the two values are the same Expression.
   126  // This includes the symbolic representation of each Expression but not their positions in the original source tree.
   127  // This does not apply any simplification to the expressions before comparing them
   128  // (for example, "!!a" wouldn't be deemed equal to "a")
   129  func ExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
   130  	return hackyExpressionsAreSame(a, b)
   131  }
   132  
   133  // TODO(jeffrygaston) once positions are removed from Expression stucts,
   134  // remove this function and have callers use reflect.DeepEqual(a, b)
   135  func hackyExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
   136  	if a.Type() != b.Type() {
   137  		return false, nil
   138  	}
   139  	left, err := hackyFingerprint(a)
   140  	if err != nil {
   141  		return false, nil
   142  	}
   143  	right, err := hackyFingerprint(b)
   144  	if err != nil {
   145  		return false, nil
   146  	}
   147  	areEqual := string(left) == string(right)
   148  	return areEqual, nil
   149  }
   150  
   151  func hackyFingerprint(expression Expression) (fingerprint []byte, err error) {
   152  	assignment := &Assignment{"a", noPos, expression, expression, noPos, "=", false}
   153  	module := &File{}
   154  	module.Defs = append(module.Defs, assignment)
   155  	p := newPrinter(module)
   156  	return p.Print()
   157  }
   158  
   159  type Type int
   160  
   161  const (
   162  	BoolType Type = iota + 1
   163  	StringType
   164  	Int64Type
   165  	ListType
   166  	MapType
   167  )
   168  
   169  func (t Type) String() string {
   170  	switch t {
   171  	case BoolType:
   172  		return "bool"
   173  	case StringType:
   174  		return "string"
   175  	case Int64Type:
   176  		return "int64"
   177  	case ListType:
   178  		return "list"
   179  	case MapType:
   180  		return "map"
   181  	default:
   182  		panic(fmt.Errorf("Unknown type %d", t))
   183  	}
   184  }
   185  
   186  type Operator struct {
   187  	Args        [2]Expression
   188  	Operator    rune
   189  	OperatorPos scanner.Position
   190  	Value       Expression
   191  }
   192  
   193  func (x *Operator) Copy() Expression {
   194  	ret := *x
   195  	ret.Args[0] = x.Args[0].Copy()
   196  	ret.Args[1] = x.Args[1].Copy()
   197  	return &ret
   198  }
   199  
   200  func (x *Operator) Eval() Expression {
   201  	return x.Value.Eval()
   202  }
   203  
   204  func (x *Operator) Type() Type {
   205  	return x.Args[0].Type()
   206  }
   207  
   208  func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() }
   209  func (x *Operator) End() scanner.Position { return x.Args[1].End() }
   210  
   211  func (x *Operator) String() string {
   212  	return fmt.Sprintf("(%s %c %s = %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(),
   213  		x.Value, x.OperatorPos)
   214  }
   215  
   216  type Variable struct {
   217  	Name    string
   218  	NamePos scanner.Position
   219  	Value   Expression
   220  }
   221  
   222  func (x *Variable) Pos() scanner.Position { return x.NamePos }
   223  func (x *Variable) End() scanner.Position { return endPos(x.NamePos, len(x.Name)) }
   224  
   225  func (x *Variable) Copy() Expression {
   226  	ret := *x
   227  	return &ret
   228  }
   229  
   230  func (x *Variable) Eval() Expression {
   231  	return x.Value.Eval()
   232  }
   233  
   234  func (x *Variable) String() string {
   235  	return x.Name + " = " + x.Value.String()
   236  }
   237  
   238  func (x *Variable) Type() Type { return x.Value.Type() }
   239  
   240  type Map struct {
   241  	LBracePos  scanner.Position
   242  	RBracePos  scanner.Position
   243  	Properties []*Property
   244  }
   245  
   246  func (x *Map) Pos() scanner.Position { return x.LBracePos }
   247  func (x *Map) End() scanner.Position { return endPos(x.RBracePos, 1) }
   248  
   249  func (x *Map) Copy() Expression {
   250  	ret := *x
   251  	ret.Properties = make([]*Property, len(x.Properties))
   252  	for i := range x.Properties {
   253  		ret.Properties[i] = x.Properties[i].Copy()
   254  	}
   255  	return &ret
   256  }
   257  
   258  func (x *Map) Eval() Expression {
   259  	return x
   260  }
   261  
   262  func (x *Map) String() string {
   263  	propertyStrings := make([]string, len(x.Properties))
   264  	for i, property := range x.Properties {
   265  		propertyStrings[i] = property.String()
   266  	}
   267  	return fmt.Sprintf("@%s-%s{%s}", x.LBracePos, x.RBracePos,
   268  		strings.Join(propertyStrings, ", "))
   269  }
   270  
   271  func (x *Map) Type() Type { return MapType }
   272  
   273  // GetProperty looks for a property with the given name.
   274  // It resembles the bracket operator of a built-in Golang map.
   275  func (x *Map) GetProperty(name string) (Property *Property, found bool) {
   276  	prop, found, _ := x.getPropertyImpl(name)
   277  	return prop, found // we don't currently expose the index to callers
   278  }
   279  
   280  func (x *Map) getPropertyImpl(name string) (Property *Property, found bool, index int) {
   281  	for i, prop := range x.Properties {
   282  		if prop.Name == name {
   283  			return prop, true, i
   284  		}
   285  	}
   286  	return nil, false, -1
   287  }
   288  
   289  // GetProperty removes the property with the given name, if it exists.
   290  func (x *Map) RemoveProperty(propertyName string) (removed bool) {
   291  	_, found, index := x.getPropertyImpl(propertyName)
   292  	if found {
   293  		x.Properties = append(x.Properties[:index], x.Properties[index+1:]...)
   294  	}
   295  	return found
   296  }
   297  
   298  type List struct {
   299  	LBracePos scanner.Position
   300  	RBracePos scanner.Position
   301  	Values    []Expression
   302  }
   303  
   304  func (x *List) Pos() scanner.Position { return x.LBracePos }
   305  func (x *List) End() scanner.Position { return endPos(x.RBracePos, 1) }
   306  
   307  func (x *List) Copy() Expression {
   308  	ret := *x
   309  	ret.Values = make([]Expression, len(x.Values))
   310  	for i := range ret.Values {
   311  		ret.Values[i] = x.Values[i].Copy()
   312  	}
   313  	return &ret
   314  }
   315  
   316  func (x *List) Eval() Expression {
   317  	return x
   318  }
   319  
   320  func (x *List) String() string {
   321  	valueStrings := make([]string, len(x.Values))
   322  	for i, value := range x.Values {
   323  		valueStrings[i] = value.String()
   324  	}
   325  	return fmt.Sprintf("@%s-%s[%s]", x.LBracePos, x.RBracePos,
   326  		strings.Join(valueStrings, ", "))
   327  }
   328  
   329  func (x *List) Type() Type { return ListType }
   330  
   331  type String struct {
   332  	LiteralPos scanner.Position
   333  	Value      string
   334  }
   335  
   336  func (x *String) Pos() scanner.Position { return x.LiteralPos }
   337  func (x *String) End() scanner.Position { return endPos(x.LiteralPos, len(x.Value)+2) }
   338  
   339  func (x *String) Copy() Expression {
   340  	ret := *x
   341  	return &ret
   342  }
   343  
   344  func (x *String) Eval() Expression {
   345  	return x
   346  }
   347  
   348  func (x *String) String() string {
   349  	return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
   350  }
   351  
   352  func (x *String) Type() Type {
   353  	return StringType
   354  }
   355  
   356  type Int64 struct {
   357  	LiteralPos scanner.Position
   358  	Value      int64
   359  	Token      string
   360  }
   361  
   362  func (x *Int64) Pos() scanner.Position { return x.LiteralPos }
   363  func (x *Int64) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) }
   364  
   365  func (x *Int64) Copy() Expression {
   366  	ret := *x
   367  	return &ret
   368  }
   369  
   370  func (x *Int64) Eval() Expression {
   371  	return x
   372  }
   373  
   374  func (x *Int64) String() string {
   375  	return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
   376  }
   377  
   378  func (x *Int64) Type() Type {
   379  	return Int64Type
   380  }
   381  
   382  type Bool struct {
   383  	LiteralPos scanner.Position
   384  	Value      bool
   385  	Token      string
   386  }
   387  
   388  func (x *Bool) Pos() scanner.Position { return x.LiteralPos }
   389  func (x *Bool) End() scanner.Position { return endPos(x.LiteralPos, len(x.Token)) }
   390  
   391  func (x *Bool) Copy() Expression {
   392  	ret := *x
   393  	return &ret
   394  }
   395  
   396  func (x *Bool) Eval() Expression {
   397  	return x
   398  }
   399  
   400  func (x *Bool) String() string {
   401  	return fmt.Sprintf("%t@%s", x.Value, x.LiteralPos)
   402  }
   403  
   404  func (x *Bool) Type() Type {
   405  	return BoolType
   406  }
   407  
   408  type CommentGroup struct {
   409  	Comments []*Comment
   410  }
   411  
   412  func (x *CommentGroup) Pos() scanner.Position { return x.Comments[0].Pos() }
   413  func (x *CommentGroup) End() scanner.Position { return x.Comments[len(x.Comments)-1].End() }
   414  
   415  type Comment struct {
   416  	Comment []string
   417  	Slash   scanner.Position
   418  }
   419  
   420  func (c Comment) Pos() scanner.Position {
   421  	return c.Slash
   422  }
   423  
   424  func (c Comment) End() scanner.Position {
   425  	pos := c.Slash
   426  	for _, comment := range c.Comment {
   427  		pos.Offset += len(comment) + 1
   428  		pos.Column = len(comment) + 1
   429  	}
   430  	pos.Line += len(c.Comment) - 1
   431  	return pos
   432  }
   433  
   434  func (c Comment) String() string {
   435  	l := 0
   436  	for _, comment := range c.Comment {
   437  		l += len(comment) + 1
   438  	}
   439  	buf := make([]byte, 0, l)
   440  	for _, comment := range c.Comment {
   441  		buf = append(buf, comment...)
   442  		buf = append(buf, '\n')
   443  	}
   444  
   445  	return string(buf) + "@" + c.Slash.String()
   446  }
   447  
   448  // Return the text of the comment with // or /* and */ stripped
   449  func (c Comment) Text() string {
   450  	l := 0
   451  	for _, comment := range c.Comment {
   452  		l += len(comment) + 1
   453  	}
   454  	buf := make([]byte, 0, l)
   455  
   456  	blockComment := false
   457  	if strings.HasPrefix(c.Comment[0], "/*") {
   458  		blockComment = true
   459  	}
   460  
   461  	for i, comment := range c.Comment {
   462  		if blockComment {
   463  			if i == 0 {
   464  				comment = strings.TrimPrefix(comment, "/*")
   465  			}
   466  			if i == len(c.Comment)-1 {
   467  				comment = strings.TrimSuffix(comment, "*/")
   468  			}
   469  		} else {
   470  			comment = strings.TrimPrefix(comment, "//")
   471  		}
   472  		buf = append(buf, comment...)
   473  		buf = append(buf, '\n')
   474  	}
   475  
   476  	return string(buf)
   477  }
   478  
   479  func endPos(pos scanner.Position, n int) scanner.Position {
   480  	pos.Offset += n
   481  	pos.Column += n
   482  	return pos
   483  }