package ast

import (
	"fmt"
)

type OperatorPrecedence int

const (
	// Expression:
	//     AssignmentExpression
	//     Expression `,` AssignmentExpression
	OperatorPrecedenceComma OperatorPrecedence = iota
	// NOTE: `Spread` is higher than `Comma` due to how it is parsed in |ElementList|
	// SpreadElement:
	//     `...` AssignmentExpression
	OperatorPrecedenceSpread
	// AssignmentExpression:
	//     ConditionalExpression
	//     YieldExpression
	//     ArrowFunction
	//     AsyncArrowFunction
	//     LeftHandSideExpression `=` AssignmentExpression
	//     LeftHandSideExpression AssignmentOperator AssignmentExpression
	//
	// NOTE: AssignmentExpression is broken down into several precedences due to the requirements
	//       of the parenthesizer rules.
	// AssignmentExpression: YieldExpression
	// YieldExpression:
	//     `yield`
	//     `yield` AssignmentExpression
	//     `yield` `*` AssignmentExpression
	OperatorPrecedenceYield
	// AssignmentExpression: LeftHandSideExpression `=` AssignmentExpression
	// AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression
	// AssignmentOperator: one of
	//     `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `**=`
	OperatorPrecedenceAssignment
	// NOTE: `Conditional` is considered higher than `Assignment` here, but in reality they have
	//       the same precedence.
	// AssignmentExpression: ConditionalExpression
	// ConditionalExpression:
	//     ShortCircuitExpression
	//     ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression
	OperatorPrecedenceConditional
	// LogicalORExpression:
	//     LogicalANDExpression
	//     LogicalORExpression `||` LogicalANDExpression
	OperatorPrecedenceLogicalOR
	// LogicalANDExpression:
	//     BitwiseORExpression
	//     LogicalANDExprerssion `&&` BitwiseORExpression
	OperatorPrecedenceLogicalAND
	// BitwiseORExpression:
	//     BitwiseXORExpression
	//     BitwiseORExpression `|` BitwiseXORExpression
	OperatorPrecedenceBitwiseOR
	// BitwiseXORExpression:
	//     BitwiseANDExpression
	//     BitwiseXORExpression `^` BitwiseANDExpression
	OperatorPrecedenceBitwiseXOR
	// BitwiseANDExpression:
	//     EqualityExpression
	//     BitwiseANDExpression `&` EqualityExpression
	OperatorPrecedenceBitwiseAND
	// EqualityExpression:
	//     RelationalExpression
	//     EqualityExpression `==` RelationalExpression
	//     EqualityExpression `!=` RelationalExpression
	//     EqualityExpression `===` RelationalExpression
	//     EqualityExpression `!==` RelationalExpression
	OperatorPrecedenceEquality
	// RelationalExpression:
	//     ShiftExpression
	//     RelationalExpression `<` ShiftExpression
	//     RelationalExpression `>` ShiftExpression
	//     RelationalExpression `<=` ShiftExpression
	//     RelationalExpression `>=` ShiftExpression
	//     RelationalExpression `instanceof` ShiftExpression
	//     RelationalExpression `in` ShiftExpression
	//     [+TypeScript] RelationalExpression `as` Type
	OperatorPrecedenceRelational
	// ShiftExpression:
	//     AdditiveExpression
	//     ShiftExpression `<<` AdditiveExpression
	//     ShiftExpression `>>` AdditiveExpression
	//     ShiftExpression `>>>` AdditiveExpression
	OperatorPrecedenceShift
	// AdditiveExpression:
	//     MultiplicativeExpression
	//     AdditiveExpression `+` MultiplicativeExpression
	//     AdditiveExpression `-` MultiplicativeExpression
	OperatorPrecedenceAdditive
	// MultiplicativeExpression:
	//     ExponentiationExpression
	//     MultiplicativeExpression MultiplicativeOperator ExponentiationExpression
	// MultiplicativeOperator: one of `*`, `/`, `%`
	OperatorPrecedenceMultiplicative
	// ExponentiationExpression:
	//     UnaryExpression
	//     UpdateExpression `**` ExponentiationExpression
	OperatorPrecedenceExponentiation
	// UnaryExpression:
	//     UpdateExpression
	//     `delete` UnaryExpression
	//     `void` UnaryExpression
	//     `typeof` UnaryExpression
	//     `+` UnaryExpression
	//     `-` UnaryExpression
	//     `~` UnaryExpression
	//     `!` UnaryExpression
	//     AwaitExpression
	// UpdateExpression:            // TODO: Do we need to investigate the precedence here?
	//     `++` UnaryExpression
	//     `--` UnaryExpression
	OperatorPrecedenceUnary
	// UpdateExpression:
	//     LeftHandSideExpression
	//     LeftHandSideExpression `++`
	//     LeftHandSideExpression `--`
	OperatorPrecedenceUpdate
	// LeftHandSideExpression:
	//     NewExpression
	// NewExpression:
	//     MemberExpression
	//     `new` NewExpression
	OperatorPrecedenceLeftHandSide
	// LeftHandSideExpression:
	//     OptionalExpression
	// OptionalExpression:
	//     MemberExpression OptionalChain
	//     CallExpression OptionalChain
	//     OptionalExpression OptionalChain
	OperatorPrecedenceOptionalChain
	// LeftHandSideExpression:
	//     CallExpression
	// CallExpression:
	//     CoverCallExpressionAndAsyncArrowHead
	//     SuperCall
	//     ImportCall
	//     CallExpression Arguments
	//     CallExpression `[` Expression `]`
	//     CallExpression `.` IdentifierName
	//     CallExpression TemplateLiteral
	// MemberExpression:
	//     PrimaryExpression
	//     MemberExpression `[` Expression `]`
	//     MemberExpression `.` IdentifierName
	//     MemberExpression TemplateLiteral
	//     SuperProperty
	//     MetaProperty
	//     `new` MemberExpression Arguments
	OperatorPrecedenceMember
	// TODO: JSXElement?
	// PrimaryExpression:
	//     `this`
	//     IdentifierReference
	//     Literal
	//     ArrayLiteral
	//     ObjectLiteral
	//     FunctionExpression
	//     ClassExpression
	//     GeneratorExpression
	//     AsyncFunctionExpression
	//     AsyncGeneratorExpression
	//     RegularExpressionLiteral
	//     TemplateLiteral
	OperatorPrecedencePrimary
	// PrimaryExpression:
	//     CoverParenthesizedExpressionAndArrowParameterList
	OperatorPrecedenceParentheses
	OperatorPrecedenceLowest        = OperatorPrecedenceComma
	OperatorPrecedenceHighest       = OperatorPrecedenceParentheses
	OperatorPrecedenceDisallowComma = OperatorPrecedenceYield
	// ShortCircuitExpression:
	//     LogicalORExpression
	//     CoalesceExpression
	// CoalesceExpression:
	//     CoalesceExpressionHead `??` BitwiseORExpression
	// CoalesceExpressionHead:
	//     CoalesceExpression
	//     BitwiseORExpression
	OperatorPrecedenceCoalesce = OperatorPrecedenceLogicalOR
	// -1 is lower than all other precedences. Returning it will cause binary expression
	// parsing to stop.
	OperatorPrecedenceInvalid OperatorPrecedence = -1
)

func getOperator(expression *Expression) Kind {
	switch expression.Kind {
	case KindBinaryExpression:
		return expression.AsBinaryExpression().OperatorToken.Kind
	case KindPrefixUnaryExpression:
		return expression.AsPrefixUnaryExpression().Operator
	case KindPostfixUnaryExpression:
		return expression.AsPostfixUnaryExpression().Operator
	default:
		return expression.Kind
	}
}

// Gets the precedence of an expression
func GetExpressionPrecedence(expression *Expression) OperatorPrecedence {
	operator := getOperator(expression)
	var flags OperatorPrecedenceFlags
	if expression.Kind == KindNewExpression && expression.AsNewExpression().Arguments == nil {
		flags = OperatorPrecedenceFlagsNewWithoutArguments
	} else if IsOptionalChain(expression) {
		flags = OperatorPrecedenceFlagsOptionalChain
	}
	return GetOperatorPrecedence(expression.Kind, operator, flags)
}

type OperatorPrecedenceFlags int

const (
	OperatorPrecedenceFlagsNone                OperatorPrecedenceFlags = 0
	OperatorPrecedenceFlagsNewWithoutArguments OperatorPrecedenceFlags = 1 << 0
	OperatorPrecedenceFlagsOptionalChain       OperatorPrecedenceFlags = 1 << 1
)

// Gets the precedence of an operator
func GetOperatorPrecedence(nodeKind Kind, operatorKind Kind, flags OperatorPrecedenceFlags) OperatorPrecedence {
	switch nodeKind {
	case KindCommaListExpression:
		return OperatorPrecedenceComma
	case KindSpreadElement:
		return OperatorPrecedenceSpread
	case KindYieldExpression:
		return OperatorPrecedenceYield
	// !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting
	case KindArrowFunction:
		return OperatorPrecedenceAssignment
	case KindConditionalExpression:
		return OperatorPrecedenceConditional
	case KindBinaryExpression:
		switch operatorKind {
		case KindCommaToken:
			return OperatorPrecedenceComma

		case KindEqualsToken,
			KindPlusEqualsToken,
			KindMinusEqualsToken,
			KindAsteriskAsteriskEqualsToken,
			KindAsteriskEqualsToken,
			KindSlashEqualsToken,
			KindPercentEqualsToken,
			KindLessThanLessThanEqualsToken,
			KindGreaterThanGreaterThanEqualsToken,
			KindGreaterThanGreaterThanGreaterThanEqualsToken,
			KindAmpersandEqualsToken,
			KindCaretEqualsToken,
			KindBarEqualsToken,
			KindBarBarEqualsToken,
			KindAmpersandAmpersandEqualsToken,
			KindQuestionQuestionEqualsToken:
			return OperatorPrecedenceAssignment

		default:
			return GetBinaryOperatorPrecedence(operatorKind)
		}
	// TODO: Should prefix `++` and `--` be moved to the `Update` precedence?
	case KindTypeAssertionExpression,
		KindNonNullExpression,
		KindPrefixUnaryExpression,
		KindTypeOfExpression,
		KindVoidExpression,
		KindDeleteExpression,
		KindAwaitExpression:
		return OperatorPrecedenceUnary

	case KindPostfixUnaryExpression:
		return OperatorPrecedenceUpdate

	// !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting
	case KindPropertyAccessExpression, KindElementAccessExpression:
		if flags&OperatorPrecedenceFlagsOptionalChain != 0 {
			return OperatorPrecedenceOptionalChain
		}
		return OperatorPrecedenceMember

	case KindCallExpression:
		if flags&OperatorPrecedenceFlagsOptionalChain != 0 {
			return OperatorPrecedenceOptionalChain
		}
		return OperatorPrecedenceMember

	// !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting
	case KindNewExpression:
		if flags&OperatorPrecedenceFlagsNewWithoutArguments != 0 {
			return OperatorPrecedenceLeftHandSide
		}
		return OperatorPrecedenceMember

	// !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting
	case KindTaggedTemplateExpression, KindMetaProperty, KindExpressionWithTypeArguments:
		return OperatorPrecedenceMember

	case KindAsExpression,
		KindSatisfiesExpression:
		return OperatorPrecedenceRelational

	case KindThisKeyword,
		KindSuperKeyword,
		KindImportKeyword,
		KindIdentifier,
		KindPrivateIdentifier,
		KindNullKeyword,
		KindTrueKeyword,
		KindFalseKeyword,
		KindNumericLiteral,
		KindBigIntLiteral,
		KindStringLiteral,
		KindArrayLiteralExpression,
		KindObjectLiteralExpression,
		KindFunctionExpression,
		KindClassExpression,
		KindRegularExpressionLiteral,
		KindNoSubstitutionTemplateLiteral,
		KindTemplateExpression,
		KindOmittedExpression,
		KindJsxElement,
		KindJsxSelfClosingElement,
		KindJsxFragment:
		return OperatorPrecedencePrimary

	// !!! By necessity, this differs from the old compiler to support emit. consider backporting
	case KindParenthesizedExpression:
		return OperatorPrecedenceParentheses

	default:
		return OperatorPrecedenceInvalid
	}
}

// Gets the precedence of a binary operator
func GetBinaryOperatorPrecedence(operatorKind Kind) OperatorPrecedence {
	switch operatorKind {
	case KindQuestionQuestionToken:
		return OperatorPrecedenceCoalesce
	case KindBarBarToken:
		return OperatorPrecedenceLogicalOR
	case KindAmpersandAmpersandToken:
		return OperatorPrecedenceLogicalAND
	case KindBarToken:
		return OperatorPrecedenceBitwiseOR
	case KindCaretToken:
		return OperatorPrecedenceBitwiseXOR
	case KindAmpersandToken:
		return OperatorPrecedenceBitwiseAND
	case KindEqualsEqualsToken, KindExclamationEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken:
		return OperatorPrecedenceEquality
	case KindLessThanToken, KindGreaterThanToken, KindLessThanEqualsToken, KindGreaterThanEqualsToken,
		KindInstanceOfKeyword, KindInKeyword, KindAsKeyword, KindSatisfiesKeyword:
		return OperatorPrecedenceRelational
	case KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken:
		return OperatorPrecedenceShift
	case KindPlusToken, KindMinusToken:
		return OperatorPrecedenceAdditive
	case KindAsteriskToken, KindSlashToken, KindPercentToken:
		return OperatorPrecedenceMultiplicative
	case KindAsteriskAsteriskToken:
		return OperatorPrecedenceExponentiation
	}
	// -1 is lower than all other precedences.  Returning it will cause binary expression
	// parsing to stop.
	return OperatorPrecedenceInvalid
}

// Gets the leftmost expression of an expression, e.g. `a` in `a.b`, `a[b]`, `a++`, `a+b`, `a?b:c`, `a as B`, etc.
func GetLeftmostExpression(node *Expression, stopAtCallExpressions bool) *Expression {
	for {
		switch node.Kind {
		case KindPostfixUnaryExpression:
			node = node.AsPostfixUnaryExpression().Operand
			continue
		case KindBinaryExpression:
			node = node.AsBinaryExpression().Left
			continue
		case KindConditionalExpression:
			node = node.AsConditionalExpression().Condition
			continue
		case KindTaggedTemplateExpression:
			node = node.AsTaggedTemplateExpression().Tag
			continue
		case KindCallExpression:
			if stopAtCallExpressions {
				return node
			}
			fallthrough
		case KindAsExpression,
			KindElementAccessExpression,
			KindPropertyAccessExpression,
			KindNonNullExpression,
			KindPartiallyEmittedExpression,
			KindSatisfiesExpression:
			node = node.Expression()
			continue
		}
		return node
	}
}

type TypePrecedence int32

const (
	// Conditional precedence (lowest)
	//
	//   Type[Extends]:
	//       ConditionalType[?Extends]
	//
	//   ConditionalType[Extends]:
	//       [~Extends] UnionType `extends` Type[+Extends] `?` Type[~Extends] `:` Type[~Extends]
	//
	TypePrecedenceConditional TypePrecedence = iota

	// JSDoc precedence (optional and variadic types)
	//
	//    JSDocType:
	//      `...`? Type `=`?
	TypePrecedenceJSDoc

	// Function precedence
	//
	//   Type[Extends]:
	//       ConditionalType[?Extends]
	//       FunctionType[?Extends]
	//       ConstructorType[?Extends]
	//
	//   ConditionalType[Extends]:
	//       UnionType
	//
	//   FunctionType[Extends]:
	//       TypeParameters? ArrowParameters `=>` Type[?Extends]
	//
	//   ConstructorType[Extends]:
	//       `abstract`? TypeParameters? ArrowParameters `=>` Type[?Extends]
	//
	TypePrecedenceFunction

	// Union precedence
	//
	//   UnionType:
	//       `|`? UnionTypeNoBar
	//
	//   UnionTypeNoBar:
	//       IntersectionType
	//       UnionTypeNoBar `|` IntersectionType
	//
	TypePrecedenceUnion

	// Intersection precedence
	//
	//   IntersectionType:
	//       `&`? IntersectionTypeNoAmpersand
	//
	//   IntersectionTypeNoAmpersand:
	//       TypeOperator
	//       IntersectionTypeNoAmpersand `&` TypeOperator
	//
	TypePrecedenceIntersection

	// TypeOperator precedence
	//
	//   TypeOperator:
	//     PostfixType
	//     InferType
	//     `keyof` TypeOperator
	//     `unique` TypeOperator
	//     `readonly` PostfixType
	//
	//   InferType:
	//     `infer` BindingIdentifier
	//     `infer` BindingIdentifier `extends` Type[+Extends]
	//
	TypePrecedenceTypeOperator

	// Postfix precedence
	//
	//   PostfixType:
	//       NonArrayType
	//       OptionalType
	//       ArrayType
	//       IndexedAccessType
	//
	//   OptionalType:
	//       PostfixType `?`
	//
	//   ArrayType:
	//       PostfixType `[` `]`
	//
	//   IndexedAccessType:
	//       PostfixType `[` Type[~Extends] `]`
	//
	TypePrecedencePostfix

	// NonArray precedence (highest)
	//
	//   NonArrayType:
	//       KeywordType
	//       LiteralType
	//       ThisType
	//       ImportType
	//       TypeQuery
	//       MappedType
	//       TypeLiteral
	//       TupleType
	//       ParenthesizedType
	//       TypePredicate
	//       TypeReference
	//       TemplateType
	//
	//   KeywordType: one of
	//       `any`       `unknown` `string`    `number` `bigint`
	//       `symbol`    `boolean` `undefined` `never`  `object`
	//       `intrinsic` `void`
	//
	//   LiteralType:
	//       StringLiteral
	//       NoSubstitutionTemplateLiteral
	//       NumericLiteral
	//       BigIntLiteral
	//       `-` NumericLiteral
	//       `-` BigIntLiteral
	//       `true`
	//       `false`
	//       `null`
	//
	//   ThisType:
	//       `this`
	//
	//   ImportType:
	//       `typeof`? `import` `(` Type[~Extends] `,`? `)` ImportTypeQualifier? TypeArguments?
	//       `typeof`? `import` `(` Type[~Extends] `,` ImportTypeAttributes `,`? `)` ImportTypeQualifier? TypeArguments?
	//
	//   ImportTypeQualifier:
	//       `.` EntityName
	//
	//   ImportTypeAttributes:
	//       `{` `with` `:` ImportAttributes `,`? `}`
	//
	//   TypeQuery:
	//
	//   MappedType:
	//       `{` MappedTypePrefix? MappedTypePropertyName MappedTypeSuffix? `:` Type[~Extends] `;` `}`
	//
	//   MappedTypePrefix:
	//       `readonly`
	//       `+` `readonly`
	//       `-` `readonly`
	//
	//   MappedTypePropertyName:
	//       `[` BindingIdentifier `in` Type[~Extends] `]`
	//       `[` BindingIdentifier `in` Type[~Extends] `as` Type[~Extends] `]`
	//
	//   MappedTypeSuffix:
	//       `?`
	//       `+` `?`
	//       `-` `?`
	//
	//   TypeLiteral:
	//       `{` TypeElementList `}`
	//
	//   TypeElementList:
	//       [empty]
	//       TypeElementList TypeElement
	//
	//   TypeElement:
	//       PropertySignature
	//       MethodSignature
	//       IndexSignature
	//       CallSignature
	//       ConstructSignature
	//
	//   PropertySignature:
	//       PropertyName `?`? TypeAnnotation? `;`
	//
	//   MethodSignature:
	//       PropertyName `?`? TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;`
	//       `get` PropertyName TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` // GetAccessor
	//       `set` PropertyName TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` // SetAccessor
	//
	//   IndexSignature:
	//       `[` IdentifierName`]` TypeAnnotation `;`
	//
	//   CallSignature:
	//       TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;`
	//
	//   ConstructSignature:
	//       `new` TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;`
	//
	//   TupleType:
	//       `[` `]`
	//       `[` NamedTupleElementTypes `,`? `]`
	//       `[` TupleElementTypes `,`? `]`
	//
	//   NamedTupleElementTypes:
	//       NamedTupleMember
	//       NamedTupleElementTypes `,` NamedTupleMember
	//
	//   NamedTupleMember:
	//       IdentifierName `?`? `:` Type[~Extends]
	//       `...` IdentifierName `:` Type[~Extends]
	//
	//   TupleElementTypes:
	//       TupleElementType
	//       TupleElementTypes `,` TupleElementType
	//
	//   TupleElementType:
	//       Type[~Extends]
	//       OptionalType
	//       RestType
	//
	//   RestType:
	//       `...` Type[~Extends]
	//
	//   ParenthesizedType:
	//       `(` Type[~Extends] `)`
	//
	//   TypePredicate:
	//       `asserts`? TypePredicateParameterName
	//       `asserts`? TypePredicateParameterName `is` Type[~Extends]
	//
	//   TypePredicateParameterName:
	//       `this`
	//       IdentifierReference
	//
	//   TypeReference:
	//       EntityName TypeArguments?
	//
	//   TemplateType:
	//       TemplateHead Type[~Extends] TemplateTypeSpans
	//
	//   TemplateTypeSpans:
	//       TemplateTail
	//       TemplateTypeMiddleList TemplateTail
	//
	//   TemplateTypeMiddleList:
	//       TemplateMiddle Type[~Extends]
	//       TemplateTypeMiddleList TemplateMiddle Type[~Extends]
	//
	//   TypeArguments:
	//       `<` TypeArgumentList `,`? `>`
	//
	//   TypeArgumentList:
	//       Type[~Extends]
	//       TypeArgumentList `,` Type[~Extends]
	//
	TypePrecedenceNonArray

	TypePrecedenceLowest  = TypePrecedenceConditional
	TypePrecedenceHighest = TypePrecedenceNonArray
)

// Gets the precedence of a TypeNode
func GetTypeNodePrecedence(n *TypeNode) TypePrecedence {
	switch n.Kind {
	case KindConditionalType:
		return TypePrecedenceConditional
	case KindJSDocOptionalType, KindJSDocVariadicType:
		return TypePrecedenceJSDoc
	case KindFunctionType, KindConstructorType:
		return TypePrecedenceFunction
	case KindUnionType:
		return TypePrecedenceUnion
	case KindIntersectionType:
		return TypePrecedenceIntersection
	case KindTypeOperator:
		return TypePrecedenceTypeOperator
	case KindInferType:
		if n.AsInferTypeNode().TypeParameter.AsTypeParameter().Constraint != nil {
			// `infer T extends U` must be treated as FunctionType precedence as the `extends` clause eagerly consumes
			// TypeNode
			return TypePrecedenceFunction
		}
		return TypePrecedenceTypeOperator
	case KindIndexedAccessType, KindArrayType, KindOptionalType:
		return TypePrecedencePostfix
	case KindTypeQuery:
		// TypeQuery is actually a NonArrayType, but we treat it as the same
		// precedence as PostfixType
		return TypePrecedencePostfix
	case KindAnyKeyword,
		KindUnknownKeyword,
		KindStringKeyword,
		KindNumberKeyword,
		KindBigIntKeyword,
		KindSymbolKeyword,
		KindBooleanKeyword,
		KindUndefinedKeyword,
		KindNeverKeyword,
		KindObjectKeyword,
		KindIntrinsicKeyword,
		KindVoidKeyword,
		KindJSDocAllType,
		KindJSDocNullableType,
		KindJSDocNonNullableType,
		KindLiteralType,
		KindTypePredicate,
		KindTypeReference,
		KindTypeLiteral,
		KindTupleType,
		KindRestType,
		KindParenthesizedType,
		KindThisType,
		KindMappedType,
		KindNamedTupleMember,
		KindTemplateLiteralType,
		KindImportType:
		return TypePrecedenceNonArray
	default:
		panic(fmt.Sprintf("unhandled TypeNode: %v", n.Kind))
	}
}
