Source file 
src/go/ast/directive.go
     1  
     2  
     3  
     4  
     5  package ast
     6  
     7  import (
     8  	"fmt"
     9  	"go/token"
    10  	"strconv"
    11  	"strings"
    12  	"unicode"
    13  	"unicode/utf8"
    14  )
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    24  
    25  
    26  
    27  
    28  
    29  
    30  
    31  
    32  type Directive struct {
    33  	Tool string
    34  	Name string
    35  	Args string 
    36  
    37  	
    38  	Slash token.Pos
    39  
    40  	
    41  	
    42  	ArgsPos token.Pos
    43  }
    44  
    45  
    46  
    47  
    48  
    49  
    50  
    51  
    52  
    53  
    54  
    55  
    56  func ParseDirective(pos token.Pos, c string) (Directive, bool) {
    57  	
    58  	
    59  	if !(len(c) >= 3 && c[0] == '/' && c[1] == '/' && isalnum(c[2])) {
    60  		return Directive{}, false
    61  	}
    62  
    63  	buf := directiveScanner{c, pos}
    64  	buf.skip(len("//"))
    65  
    66  	
    67  	
    68  	
    69  	
    70  	colon := strings.Index(buf.str, ":")
    71  	if colon <= 0 || colon+1 >= len(buf.str) {
    72  		return Directive{}, false
    73  	}
    74  	for i := 0; i <= colon+1; i++ {
    75  		if i == colon {
    76  			continue
    77  		}
    78  		if !isalnum(buf.str[i]) {
    79  			return Directive{}, false
    80  		}
    81  	}
    82  	tool := buf.take(colon)
    83  	buf.skip(len(":"))
    84  
    85  	
    86  	name := buf.takeNonSpace()
    87  	buf.skipSpace()
    88  	argsPos := buf.pos
    89  	args := strings.TrimRightFunc(buf.str, unicode.IsSpace)
    90  
    91  	return Directive{tool, name, args, pos, argsPos}, true
    92  }
    93  
    94  func isalnum(b byte) bool {
    95  	return 'a' <= b && b <= 'z' || '0' <= b && b <= '9'
    96  }
    97  
    98  func (d *Directive) Pos() token.Pos { return d.Slash }
    99  func (d *Directive) End() token.Pos { return token.Pos(int(d.ArgsPos) + len(d.Args)) }
   100  
   101  
   102  type DirectiveArg struct {
   103  	
   104  	
   105  	Arg string
   106  	
   107  	Pos token.Pos
   108  }
   109  
   110  
   111  
   112  
   113  
   114  
   115  
   116  func (d *Directive) ParseArgs() ([]DirectiveArg, error) {
   117  	args := directiveScanner{d.Args, d.ArgsPos}
   118  
   119  	list := []DirectiveArg{}
   120  	for args.skipSpace(); args.str != ""; args.skipSpace() {
   121  		var arg string
   122  		argPos := args.pos
   123  
   124  		switch args.str[0] {
   125  		default:
   126  			arg = args.takeNonSpace()
   127  
   128  		case '`', '"':
   129  			q, err := strconv.QuotedPrefix(args.str)
   130  			if err != nil { 
   131  				return nil, fmt.Errorf("invalid quoted string in //%s:%s: %s", d.Tool, d.Name, args.str)
   132  			}
   133  			
   134  			arg, _ = strconv.Unquote(args.take(len(q)))
   135  
   136  			
   137  			if args.str != "" {
   138  				r, _ := utf8.DecodeRuneInString(args.str)
   139  				if !unicode.IsSpace(r) {
   140  					return nil, fmt.Errorf("invalid quoted string in //%s:%s: %s", d.Tool, d.Name, args.str)
   141  				}
   142  			}
   143  		}
   144  
   145  		list = append(list, DirectiveArg{arg, argPos})
   146  	}
   147  	return list, nil
   148  }
   149  
   150  
   151  
   152  type directiveScanner struct {
   153  	str string
   154  	pos token.Pos
   155  }
   156  
   157  func (s *directiveScanner) skip(n int) {
   158  	s.pos += token.Pos(n)
   159  	s.str = s.str[n:]
   160  }
   161  
   162  func (s *directiveScanner) take(n int) string {
   163  	res := s.str[:n]
   164  	s.skip(n)
   165  	return res
   166  }
   167  
   168  func (s *directiveScanner) takeNonSpace() string {
   169  	i := strings.IndexFunc(s.str, unicode.IsSpace)
   170  	if i == -1 {
   171  		i = len(s.str)
   172  	}
   173  	return s.take(i)
   174  }
   175  
   176  func (s *directiveScanner) skipSpace() {
   177  	trim := strings.TrimLeftFunc(s.str, unicode.IsSpace)
   178  	s.skip(len(s.str) - len(trim))
   179  }
   180  
View as plain text