// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ast

import (
	
	
	
	
	
	
)

// A Directive is a comment of this form:
//
//	//tool:name args
//
// For example, this directive:
//
//	//go:generate stringer -type Op -trimprefix Op
//
// would have Tool "go", Name "generate", and Args "stringer -type Op
// -trimprefix Op".
//
// While Args does not have a strict syntax, by convention it is a
// space-separated sequence of unquoted words, '"'-quoted Go strings, or
// '`'-quoted raw strings.
//
// See https://go.dev/doc/comment#directives for specification.
type Directive struct {
	Tool string
	Name string
	Args string // no leading or trailing whitespace

	// Slash is the position of the "//" at the beginning of the directive.
	Slash token.Pos

	// ArgsPos is the position where Args begins, based on the position passed
	// to ParseDirective.
	ArgsPos token.Pos
}

// ParseDirective parses a single comment line for a directive comment.
//
// If the line is not a directive comment, it returns false.
//
// The provided text must be a single line and should include the leading "//".
// If the text does not start with "//", it returns false.
//
// The caller may provide a file position of the start of c. This will be used
// to track the position of the arguments. This may be [Comment.Slash],
// synthesized by the caller, or simply 0. If the caller passes 0, then the
// positions are effectively byte offsets into the string c.
func ( token.Pos,  string) (Directive, bool) {
	// Fast path to eliminate most non-directive comments. Must be a line
	// comment starting with [a-z0-9]
	if !(len() >= 3 && [0] == '/' && [1] == '/' && isalnum([2])) {
		return Directive{}, false
	}

	 := directiveScanner{, }
	.skip(len("//"))

	// Check for a valid directive and parse tool part.
	//
	// This logic matches isDirective. (We could combine them, but isDirective
	// itself is duplicated in several places.)
	 := strings.Index(.str, ":")
	if  <= 0 || +1 >= len(.str) {
		return Directive{}, false
	}
	for  := 0;  <= +1; ++ {
		if  ==  {
			continue
		}
		if !isalnum(.str[]) {
			return Directive{}, false
		}
	}
	 := .take()
	.skip(len(":"))

	// Parse name and args.
	 := .takeNonSpace()
	.skipSpace()
	 := .pos
	 := strings.TrimRightFunc(.str, unicode.IsSpace)

	return Directive{, , , , }, true
}

func isalnum( byte) bool {
	return 'a' <=  &&  <= 'z' || '0' <=  &&  <= '9'
}

func ( *Directive) () token.Pos { return .Slash }
func ( *Directive) () token.Pos { return token.Pos(int(.ArgsPos) + len(.Args)) }

// A DirectiveArg is an argument to a directive comment.
type DirectiveArg struct {
	// Arg is the parsed argument string. If the argument was a quoted string,
	// this is its unquoted form.
	Arg string
	// Pos is the position of the first character in this argument.
	Pos token.Pos
}

// ParseArgs parses a [Directive]'s arguments using the standard convention,
// which is a sequence of tokens, where each token may be a bare word, or a
// double quoted Go string, or a back quoted raw Go string. Each token must be
// separated by one or more Unicode spaces.
//
// If the arguments do not conform to this syntax, it returns an error.
func ( *Directive) () ([]DirectiveArg, error) {
	 := directiveScanner{.Args, .ArgsPos}

	 := []DirectiveArg{}
	for .skipSpace(); .str != ""; .skipSpace() {
		var  string
		 := .pos

		switch .str[0] {
		default:
			 = .takeNonSpace()

		case '`', '"':
			,  := strconv.QuotedPrefix(.str)
			if  != nil { // Always strconv.ErrSyntax
				return nil, fmt.Errorf("invalid quoted string in //%s:%s: %s", .Tool, .Name, .str)
			}
			// Any errors will have been returned by QuotedPrefix
			, _ = strconv.Unquote(.take(len()))

			// Check that the quoted string is followed by a space (or nothing)
			if .str != "" {
				,  := utf8.DecodeRuneInString(.str)
				if !unicode.IsSpace() {
					return nil, fmt.Errorf("invalid quoted string in //%s:%s: %s", .Tool, .Name, .str)
				}
			}
		}

		 = append(, DirectiveArg{, })
	}
	return , nil
}

// directiveScanner is a helper for parsing directive comments while maintaining
// position information.
type directiveScanner struct {
	str string
	pos token.Pos
}

func ( *directiveScanner) ( int) {
	.pos += token.Pos()
	.str = .str[:]
}

func ( *directiveScanner) ( int) string {
	 := .str[:]
	.skip()
	return 
}

func ( *directiveScanner) () string {
	 := strings.IndexFunc(.str, unicode.IsSpace)
	if  == -1 {
		 = len(.str)
	}
	return .take()
}

func ( *directiveScanner) () {
	 := strings.TrimLeftFunc(.str, unicode.IsSpace)
	.skip(len(.str) - len())
}