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

     1  // Copyright 2014 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  	"sort"
    19  	"text/scanner"
    20  )
    21  
    22  func SortLists(file *File) {
    23  	for _, def := range file.Defs {
    24  		if assignment, ok := def.(*Assignment); ok {
    25  			sortListsInValue(assignment.Value, file)
    26  		} else if module, ok := def.(*Module); ok {
    27  			for _, prop := range module.Properties {
    28  				sortListsInValue(prop.Value, file)
    29  			}
    30  		}
    31  	}
    32  	sort.Sort(commentsByOffset(file.Comments))
    33  }
    34  
    35  func SortList(file *File, list *List) {
    36  	for i := 0; i < len(list.Values); i++ {
    37  		// Find a set of values on contiguous lines
    38  		line := list.Values[i].Pos().Line
    39  		var j int
    40  		for j = i + 1; j < len(list.Values); j++ {
    41  			if list.Values[j].Pos().Line > line+1 {
    42  				break
    43  			}
    44  			line = list.Values[j].Pos().Line
    45  		}
    46  
    47  		nextPos := list.End()
    48  		if j < len(list.Values) {
    49  			nextPos = list.Values[j].Pos()
    50  		}
    51  		sortSubList(list.Values[i:j], nextPos, file)
    52  		i = j - 1
    53  	}
    54  }
    55  
    56  func ListIsSorted(list *List) bool {
    57  	for i := 0; i < len(list.Values); i++ {
    58  		// Find a set of values on contiguous lines
    59  		line := list.Values[i].Pos().Line
    60  		var j int
    61  		for j = i + 1; j < len(list.Values); j++ {
    62  			if list.Values[j].Pos().Line > line+1 {
    63  				break
    64  			}
    65  			line = list.Values[j].Pos().Line
    66  		}
    67  
    68  		if !subListIsSorted(list.Values[i:j]) {
    69  			return false
    70  		}
    71  		i = j - 1
    72  	}
    73  
    74  	return true
    75  }
    76  
    77  func sortListsInValue(value Expression, file *File) {
    78  	switch v := value.(type) {
    79  	case *Variable:
    80  		// Nothing
    81  	case *Operator:
    82  		sortListsInValue(v.Args[0], file)
    83  		sortListsInValue(v.Args[1], file)
    84  	case *Map:
    85  		for _, p := range v.Properties {
    86  			sortListsInValue(p.Value, file)
    87  		}
    88  	case *List:
    89  		SortList(file, v)
    90  	}
    91  }
    92  
    93  func sortSubList(values []Expression, nextPos scanner.Position, file *File) {
    94  	l := make(elemList, len(values))
    95  	for i, v := range values {
    96  		s, ok := v.(*String)
    97  		if !ok {
    98  			panic("list contains non-string element")
    99  		}
   100  		n := nextPos
   101  		if i < len(values)-1 {
   102  			n = values[i+1].Pos()
   103  		}
   104  		l[i] = elem{s.Value, i, v.Pos(), n}
   105  	}
   106  
   107  	sort.Sort(l)
   108  
   109  	copyValues := append([]Expression{}, values...)
   110  	copyComments := make([]*CommentGroup, len(file.Comments))
   111  	for i := range file.Comments {
   112  		cg := *file.Comments[i]
   113  		cg.Comments = make([]*Comment, len(cg.Comments))
   114  		for j := range file.Comments[i].Comments {
   115  			c := *file.Comments[i].Comments[j]
   116  			cg.Comments[j] = &c
   117  		}
   118  		copyComments[i] = &cg
   119  	}
   120  
   121  	curPos := values[0].Pos()
   122  	for i, e := range l {
   123  		values[i] = copyValues[e.i]
   124  		values[i].(*String).LiteralPos = curPos
   125  		for j, c := range copyComments {
   126  			if c.Pos().Offset > e.pos.Offset && c.Pos().Offset < e.nextPos.Offset {
   127  				file.Comments[j].Comments[0].Slash.Line = curPos.Line
   128  				file.Comments[j].Comments[0].Slash.Offset += values[i].Pos().Offset - e.pos.Offset
   129  			}
   130  		}
   131  
   132  		curPos.Offset += e.nextPos.Offset - e.pos.Offset
   133  		curPos.Line++
   134  	}
   135  }
   136  
   137  func subListIsSorted(values []Expression) bool {
   138  	prev := ""
   139  	for _, v := range values {
   140  		s, ok := v.(*String)
   141  		if !ok {
   142  			panic("list contains non-string element")
   143  		}
   144  		if prev > s.Value {
   145  			return false
   146  		}
   147  		prev = s.Value
   148  	}
   149  
   150  	return true
   151  }
   152  
   153  type elem struct {
   154  	s       string
   155  	i       int
   156  	pos     scanner.Position
   157  	nextPos scanner.Position
   158  }
   159  
   160  type elemList []elem
   161  
   162  func (l elemList) Len() int {
   163  	return len(l)
   164  }
   165  
   166  func (l elemList) Swap(i, j int) {
   167  	l[i], l[j] = l[j], l[i]
   168  }
   169  
   170  func (l elemList) Less(i, j int) bool {
   171  	return l[i].s < l[j].s
   172  }
   173  
   174  type commentsByOffset []*CommentGroup
   175  
   176  func (l commentsByOffset) Len() int {
   177  	return len(l)
   178  }
   179  
   180  func (l commentsByOffset) Less(i, j int) bool {
   181  	return l[i].Pos().Offset < l[j].Pos().Offset
   182  }
   183  
   184  func (l commentsByOffset) Swap(i, j int) {
   185  	l[i], l[j] = l[j], l[i]
   186  }