// Copyright (c) 2009-2022 The MathJax Consortium
// SPDX-License-Identifier: Apache-2.0
//
// This file is a Go translation and modification of MathJax 3.2.2.
// Sources: ts/input/tex/ParseUtil.ts, base/BaseMethods.ts, and
// newcommand/{NewcommandMethods,NewcommandUtil}.ts.

package tex

import (
	
	

	
)

func ( *parser) ( string,  macroDefinition) ([]*mml.Node, error) {
	 := .body
	if .arguments != 0 {
		if .prefix != "" {
			.skipSpaces()
			if !strings.HasPrefix(.source[.pos:], .prefix) {
				return nil, texError("MismatchUseDef", "Use of \\%s doesn't match its definition", )
			}
			.pos += len(.prefix)
		}
		 := make([]string, 0, .arguments)
		if .optionalDefault != nil {
			, ,  := .readBrackets(.optionalDefault)
			if  != nil {
				if ,  := .(*Error);  && .ID == "MissingCloseBracket" {
					return nil, texError(.ID, "Could not find closing ']' for argument to %s", "\\"+)
				}
				return nil, 
			}
			if ! {
				 = *.optionalDefault
			}
			 = append(, )
		}
		for len() < .arguments {
			var  string
			var  error
			 := len()
			if  < len(.delimiters) && .delimiters[] != "" {
				,  = .readDelimitedParameter(, .delimiters[])
			} else {
				, _,  = .readArgument(, false)
			}
			if  != nil {
				return nil, 
			}
			 = append(, )
		}
		var  error
		,  = substituteMacroArguments(, )
		if  != nil {
			return nil, 
		}
	}
	,  := macroAddArgs(, .source[.pos:], maxMacroBuffer)
	if  != nil {
		return nil, 
	}
	// Macro and MacroWithTemplate install only the unparsed program before
	// charging the counter. Existing row/prime/script state remains in place.
	.source, .pos = , 0
	.state.macroCount++
	if .state.macroCount > maxMacros {
		return nil, texError("MaxMacroSub1", "MathJax maximum macro substitution count exceeded; is here a recursive macro call?")
	}
	return nil, nil
}

func substituteArguments( string,  []string) (string, error) {
	var  strings.Builder
	for  := 0;  < len(); ++ {
		if [] != '#' {
			.WriteByte([])
			continue
		}
		if +1 >= len() {
			return "", texError("IllegalMacroParam", "Illegal macro parameter reference")
		}
		++
		if [] == '#' {
			.WriteByte('#')
			continue
		}
		if [] < '1' || [] > '9' {
			return "", texError("IllegalMacroParam", "Illegal macro parameter reference")
		}
		 := int([] - '1')
		if  >= len() {
			return "", texError("IllegalMacroParam", "Illegal macro parameter reference")
		}
		.WriteString([])
	}
	return .String(), nil
}

func ( *parser) ( string) error {
	,  := .readCSNameArgument()
	if  != nil {
		return 
	}
	, ,  := .readBrackets(nil)
	if  != nil {
		return 
	}
	 := 0
	if  {
		,  = parseInteger(, "IllegalMacroParam")
		if  != nil ||  < 0 ||  > 9 {
			return texError("IllegalMacroParam", "Illegal number of parameters specified in \\%s", )
		}
	}
	var  *string
	if  > 0 {
		if , ,  := .readBrackets(nil);  != nil {
			return 
		} else if  {
			 = &
		}
	}
	, ,  := .readArgument(, false)
	if  != nil {
		return 
	}
	if  := macroArguments();  >  {
		return texError("IllegalMacroParam", "Illegal macro parameter reference")
	}
	.state.macros[] = macroDefinition{body: , arguments: , optionalDefault: }
	return nil
}

func ( *parser) ( string) error {
	, ,  := .readArgument(, false)
	if  != nil {
		return 
	}
	 = strings.TrimSpace()
	, ,  := .readBrackets(nil)
	if  != nil {
		return 
	}
	 := 0
	if  {
		,  = parseInteger(, "IllegalMacroParam")
		if  != nil ||  < 0 ||  > 9 {
			return texError("IllegalMacroParam", "Illegal number of parameters specified in \\%s", )
		}
	}
	var  *string
	if  > 0 {
		if , ,  := .readBrackets(nil);  != nil {
			return 
		} else if  {
			 = &
		}
	}
	, ,  := .readArgument(, false)
	if  != nil {
		return 
	}
	, ,  := .readArgument(, false)
	if  != nil {
		return 
	}
	.state.environments[] = environmentDefinition{
		begin: , end: , arguments: , optionalDefault: ,
	}
	return nil
}

func ( *parser) ( string) error {
	.skipSpaces()
	if .pos >= len(.source) || .source[.pos] != '\\' {
		return texError("MissingCS", "%s must be followed by a control sequence", "\\"+)
	}
	.pos++
	 := .readControlSequence()
	 := .pos
	for .pos < len(.source) && .source[.pos] != '{' {
		if .source[.pos] == '\\' {
			.pos++
			.readControlSequence()
			continue
		}
		.consumeRune()
	}
	 := .source[:.pos]
	, ,  := .readArgument(, false)
	if  != nil {
		return 
	}
	 := macroDefinition{body: }
	, , ,  := parseParameterTemplate()
	if  != nil {
		return 
	}
	.prefix = 
	.delimiters = 
	.arguments = 
	.state.macros[] = 
	return nil
}

func parseParameterTemplate( string) (string, []string, int, error) {
	 := make([]int, 0, 9)
	for  := 0;  < len(); ++ {
		if [] != '#' {
			continue
		}
		if +1 >= len() || [+1] < '1' || [+1] > '9' {
			return "", nil, 0, texError("IllegalMacroParam", "Illegal macro parameter reference")
		}
		 := len() + 1
		if int([+1]-'0') !=  {
			return "", nil, 0, texError("IllegalMacroParam", "Parameters must be numbered consecutively")
		}
		 = append(, )
		++
	}
	if len() == 0 {
		return , nil, 0, nil
	}
	 := [:[0]]
	 := make([]string, len())
	for ,  := range  {
		 :=  + 2
		 := len()
		if +1 < len() {
			 = [+1]
		}
		[] = [:]
	}
	return , , len(), nil
}

func ( *parser) ( string) error {
	,  := .readCSName()
	if  != nil {
		return 
	}
	.skipSpaces()
	if .pos < len(.source) && .source[.pos] == '=' {
		.pos++
		.skipSpaces()
	}
	if .pos >= len(.source) {
		return nil
	}
	if .source[.pos] == '\\' {
		.pos++
		 := .readControlSequence()
		if ,  := .state.macros[];  {
			.state.macros[] = 
			return nil
		}
		if ,  := .state.pairedDelimiters[];  == "not" && ! {
			// Capture the builtin handler, not a macro that resolves \not later.
			.state.macros[] = macroDefinition{builtinNot: true}
			return nil
		}
		if ,  := identifierSymbols[];  {
			.state.macros[] = macroDefinition{body: "\\" + }
			return nil
		}
		if ,  := operatorSymbols[];  {
			.state.macros[] = macroDefinition{body: "\\" + }
			return nil
		}
		// MathJax's \let of an undefined CS is a no-op.
		return nil
	}
	 := .pos
	,  := utf8.DecodeRuneInString(.source[.pos:])
	.pos += 
	.state.macros[] = macroDefinition{body: .source[:.pos]}
	return nil
}

func ( *parser) ( string) (string, error) {
	, ,  := .readArgument(, false)
	if  != nil {
		return "", 
	}
	return validatedCSNameArgument(, )
}

// validatedCSNameArgument follows NewcommandUtil.GetCsNameArgument, whose
// non-Unicode regexp accepts a single BMP code unit or an ASCII-letter word.
func validatedCSNameArgument(,  string) (string, error) {
	 := strings.TrimFunc(, internalTextSpace)
	if strings.HasSuffix(, "\\") && strings.HasSuffix(, " ") {
		 += " " // ParseUtil.trimSpaces preserves a terminal control-space.
	}
	 = strings.TrimPrefix(, "\\")
	,  := utf8.DecodeRuneInString()
	 :=  != "" &&  == len() &&  <= 0xffff &&
		 != '\n' &&  != '\r' &&  != '\u2028' &&  != '\u2029'
	if ! &&  != "" {
		 = true
		for ,  := range  {
			if !isASCIILetter() {
				 = false
				break
			}
		}
	}
	if ! {
		return "", texError("IllegalControlSequenceName", "Illegal control sequence name for %s", "\\"+)
	}
	return , nil
}

func ( *parser) ( string) (string, error) {
	.skipSpaces()
	if .pos >= len(.source) || .source[.pos] != '\\' {
		return "", texError("MissingCS", "%s must be followed by a control sequence", "\\"+)
	}
	.pos++
	return .readControlSequence(), nil
}

func ( *parser) (,  string) (string, error) {
	.skipSpaces()
	 := .pos
	 := 0
	for .pos < len(.source) {
		if  == 0 && strings.HasPrefix(.source[.pos:], ) {
			 := .source[:.pos]
			.pos += len()
			return , nil
		}
		switch .source[.pos] {
		case '\\':
			.pos++
			.readControlSequence()
		case '{':
			++
			.pos++
		case '}':
			if  > 0 {
				--
			}
			.pos++
		default:
			.consumeRune()
		}
	}
	return "", texError("RunawayArgument", "Runaway argument for \\%s", )
}

func macroNode( []*mml.Node) *mml.Node { return row(, true) }