#
# Simple case.
#
compile
# Join comment.
define Join {
    Left  Expr
    Right Expr
}

# CommuteJoin comment.
[CommuteJoin]
(Join $left:* $right:*) => (Join $right $left)
----
(Compiled
	(Defines
		(Define
			Comments=(Comments # Join comment.)
			Tags=(Tags)
			Name="Join"
			Fields=(DefineFields
				(DefineField Name="Left" Type="Expr" Src=<test.opt:3:5>)
				(DefineField Name="Right" Type="Expr" Src=<test.opt:4:5>)
			)
			Src=<test.opt:2:1>
		)
	)
	(Rules
		(Rule
			Comments=(Comments # CommuteJoin comment.)
			Name="CommuteJoin"
			Tags=(Tags)
			Match=(Func
				Name=Join
				Args=(Slice
					(Bind Label="left" Target=(Any) Src=<test.opt:9:7>)
					(Bind Label="right" Target=(Any) Src=<test.opt:9:15>)
				)
				Src=<test.opt:9:1>
			)
			Replace=(Func
				Name=Join
				Args=(Slice
					(Ref Label="right" Src=<test.opt:9:34>)
					(Ref Label="left" Src=<test.opt:9:41>)
				)
				Src=<test.opt:9:28>
			)
			Src=<test.opt:8:1>
		)
	)
)

#
# Expand multiple match names into multiple rules and use OpName function with
# no arguments.
#
compile
[Join]
define InnerJoin {
    Left  Expr
    Right Expr
}
[Join]
define LeftJoin {
    Left  Expr
    Right Expr
}
define Project {
    Input Expr
}

# Name rule comment.
[Name]
(Join | Project * & (Func (OpName))) => ((OpName) (OpName))
----
(Compiled
	(Defines
		(Define
			Comments=(Comments)
			Tags=(Tags Join)
			Name="InnerJoin"
			Fields=(DefineFields
				(DefineField Name="Left" Type="Expr" Src=<test.opt:3:5>)
				(DefineField Name="Right" Type="Expr" Src=<test.opt:4:5>)
			)
			Src=<test.opt:1:1>
		)
		(Define
			Comments=(Comments)
			Tags=(Tags Join)
			Name="LeftJoin"
			Fields=(DefineFields
				(DefineField Name="Left" Type="Expr" Src=<test.opt:8:5>)
				(DefineField Name="Right" Type="Expr" Src=<test.opt:9:5>)
			)
			Src=<test.opt:6:1>
		)
		(Define
			Comments=(Comments)
			Tags=(Tags)
			Name="Project"
			Fields=(DefineFields
				(DefineField Name="Input" Type="Expr" Src=<test.opt:12:5>)
			)
			Src=<test.opt:11:1>
		)
	)
	(Rules
		(Rule
			Comments=(Comments # Name rule comment.)
			Name="Name"
			Tags=(Tags)
			Match=(Func
				Name=InnerJoin
				Args=(Slice
					(And
						Left=(Any)
						Right=(CustomFunc
							Name=Func
							Args=(Slice InnerJoin)
							Src=<test.opt:17:21>
						)
						Src=<test.opt:17:17>
					)
				)
				Src=<test.opt:17:1>
			)
			Replace=(Func
				Name=InnerJoin
				Args=(Slice InnerJoin)
				Src=<test.opt:17:41>
			)
			Src=<test.opt:16:1>
		)
		(Rule
			Comments=(Comments # Name rule comment.)
			Name="Name"
			Tags=(Tags)
			Match=(Func
				Name=LeftJoin
				Args=(Slice
					(And
						Left=(Any)
						Right=(CustomFunc
							Name=Func
							Args=(Slice LeftJoin)
							Src=<test.opt:17:21>
						)
						Src=<test.opt:17:17>
					)
				)
				Src=<test.opt:17:1>
			)
			Replace=(Func
				Name=LeftJoin
				Args=(Slice LeftJoin)
				Src=<test.opt:17:41>
			)
			Src=<test.opt:16:1>
		)
		(Rule
			Comments=(Comments # Name rule comment.)
			Name="Name"
			Tags=(Tags)
			Match=(Func
				Name=Project
				Args=(Slice
					(And
						Left=(Any)
						Right=(CustomFunc
							Name=Func
							Args=(Slice Project)
							Src=<test.opt:17:21>
						)
						Src=<test.opt:17:17>
					)
				)
				Src=<test.opt:17:1>
			)
			Replace=(Func
				Name=Project
				Args=(Slice Project)
				Src=<test.opt:17:41>
			)
			Src=<test.opt:16:1>
		)
	)
)

#
# Compile OpName functions with arguments.
#
compile
define Op {
    Input Expr
}
define SubOp1 {}
define SubOp2 {}

[SingleName]
(Op $input:(SubOp1) & ^(Func (OpName $input))) => ((OpName $input))

[MultipleNames]
(Op $input:(SubOp1 | SubOp2) & (Func (OpName $input))) => ((OpName $input))
----
(Compiled
	(Defines
		(Define
			Comments=(Comments)
			Tags=(Tags)
			Name="Op"
			Fields=(DefineFields
				(DefineField Name="Input" Type="Expr" Src=<test.opt:2:5>)
			)
			Src=<test.opt:1:1>
		)
		(Define Comments=(Comments) Tags=(Tags) Name="SubOp1" Fields=(DefineFields) Src=<test.opt:4:1>)
		(Define Comments=(Comments) Tags=(Tags) Name="SubOp2" Fields=(DefineFields) Src=<test.opt:5:1>)
	)
	(Rules
		(Rule
			Comments=(Comments)
			Name="SingleName"
			Tags=(Tags)
			Match=(Func
				Name=Op
				Args=(Slice
					(Bind
						Label="input"
						Target=(And
							Left=(Func Name=SubOp1 Args=(Slice) Src=<test.opt:8:12>)
							Right=(Not
								Input=(CustomFunc
									Name=Func
									Args=(Slice
										(CustomFunc
											Name=OpName
											Args=(Slice
												(Ref Label="input" Src=<test.opt:8:38>)
											)
											Src=<test.opt:8:30>
										)
									)
									Src=<test.opt:8:24>
								)
								Src=<test.opt:8:23>
							)
							Src=<test.opt:8:12>
						)
						Src=<test.opt:8:5>
					)
				)
				Src=<test.opt:8:1>
			)
			Replace=(Func
				Name=(CustomFunc
					Name=OpName
					Args=(Slice
						(Ref Label="input" Src=<test.opt:8:60>)
					)
					Src=<test.opt:8:52>
				)
				Args=(Slice)
				Src=<test.opt:8:51>
			)
			Src=<test.opt:7:1>
		)
		(Rule
			Comments=(Comments)
			Name="MultipleNames"
			Tags=(Tags)
			Match=(Func
				Name=Op
				Args=(Slice
					(Bind
						Label="input"
						Target=(And
							Left=(Func
								Name=(Names SubOp1 SubOp2)
								Args=(Slice)
								Src=<test.opt:11:12>
							)
							Right=(CustomFunc
								Name=Func
								Args=(Slice
									(CustomFunc
										Name=OpName
										Args=(Slice
											(Ref Label="input" Src=<test.opt:11:46>)
										)
										Src=<test.opt:11:38>
									)
								)
								Src=<test.opt:11:32>
							)
							Src=<test.opt:11:12>
						)
						Src=<test.opt:11:5>
					)
				)
				Src=<test.opt:11:1>
			)
			Replace=(Func
				Name=(CustomFunc
					Name=OpName
					Args=(Slice
						(Ref Label="input" Src=<test.opt:11:68>)
					)
					Src=<test.opt:11:60>
				)
				Args=(Slice)
				Src=<test.opt:11:59>
			)
			Src=<test.opt:10:1>
		)
	)
)

#
# Compile custom match function.
#
compile
define Op {
    Input Expr
}

[CustomFunc]
(Op $input:* & (Func $input (SubFunc $input (SubSubFunc)))) => $input
----
(Compiled
	(Defines
		(Define
			Comments=(Comments)
			Tags=(Tags)
			Name="Op"
			Fields=(DefineFields
				(DefineField Name="Input" Type="Expr" Src=<test.opt:2:5>)
			)
			Src=<test.opt:1:1>
		)
	)
	(Rules
		(Rule
			Comments=(Comments)
			Name="CustomFunc"
			Tags=(Tags)
			Match=(Func
				Name=Op
				Args=(Slice
					(Bind
						Label="input"
						Target=(And
							Left=(Any)
							Right=(CustomFunc
								Name=Func
								Args=(Slice
									(Ref Label="input" Src=<test.opt:6:22>)
									(CustomFunc
										Name=SubFunc
										Args=(Slice
											(Ref Label="input" Src=<test.opt:6:38>)
											(CustomFunc Name=SubSubFunc Args=(Slice) Src=<test.opt:6:45>)
										)
										Src=<test.opt:6:29>
									)
								)
								Src=<test.opt:6:16>
							)
							Src=<test.opt:6:12>
						)
						Src=<test.opt:6:5>
					)
				)
				Src=<test.opt:6:1>
			)
			Replace=(Ref Label="input" Src=<test.opt:6:64>)
			Src=<test.opt:5:1>
		)
	)
)

#
# Use string expressions with op matcher, construct, and custom functions.
#
compile
define Op {
    Input Expr
}

[Strings]
(Op $input:"foo" & (Func "bar")) => (Op (Func "bar"))
----
(Compiled
	(Defines
		(Define
			Comments=(Comments)
			Tags=(Tags)
			Name="Op"
			Fields=(DefineFields
				(DefineField Name="Input" Type="Expr" Src=<test.opt:2:5>)
			)
			Src=<test.opt:1:1>
		)
	)
	(Rules
		(Rule
			Comments=(Comments)
			Name="Strings"
			Tags=(Tags)
			Match=(Func
				Name=Op
				Args=(Slice
					(Bind
						Label="input"
						Target=(And
							Left="foo"
							Right=(CustomFunc
								Name=Func
								Args=(Slice "bar")
								Src=<test.opt:6:20>
							)
							Src=<test.opt:6:12>
						)
						Src=<test.opt:6:5>
					)
				)
				Src=<test.opt:6:1>
			)
			Replace=(Func
				Name=Op
				Args=(Slice
					(CustomFunc
						Name=Func
						Args=(Slice "bar")
						Src=<test.opt:6:41>
					)
				)
				Src=<test.opt:6:37>
			)
			Src=<test.opt:5:1>
		)
	)
)

#
# Use list expressions with match and replace functions.
#
compile
define Op {
    Input1 Expr
    Input2 Expr
    Input3 Expr
    Input4 Expr
    Input5 Expr
    Input6 Expr
}

[Lists]
(Op
    [ ... (Op) ... ]
    [ (Op) ... ]
    [ ... (Op) ]
    [ ... ... ]
    [ (Op) ]
    [ ]
)
=>
(Op
    [ (Op) (Op) ]
    [ (Op) ]
    [ ]
    (Func [ (Op) (Op) ])
)
----
(Compiled
	(Defines
		(Define
			Comments=(Comments)
			Tags=(Tags)
			Name="Op"
			Fields=(DefineFields
				(DefineField Name="Input1" Type="Expr" Src=<test.opt:2:5>)
				(DefineField Name="Input2" Type="Expr" Src=<test.opt:3:5>)
				(DefineField Name="Input3" Type="Expr" Src=<test.opt:4:5>)
				(DefineField Name="Input4" Type="Expr" Src=<test.opt:5:5>)
				(DefineField Name="Input5" Type="Expr" Src=<test.opt:6:5>)
				(DefineField Name="Input6" Type="Expr" Src=<test.opt:7:5>)
			)
			Src=<test.opt:1:1>
		)
	)
	(Rules
		(Rule
			Comments=(Comments)
			Name="Lists"
			Tags=(Tags)
			Match=(Func
				Name=Op
				Args=(Slice
					(List
						Items=(Slice
							(ListAny)
							(Func Name=Op Args=(Slice) Src=<test.opt:12:11>)
							(ListAny)
						)
						Src=<test.opt:12:5>
					)
					(List
						Items=(Slice
							(Func Name=Op Args=(Slice) Src=<test.opt:13:7>)
							(ListAny)
						)
						Src=<test.opt:13:5>
					)
					(List
						Items=(Slice
							(ListAny)
							(Func Name=Op Args=(Slice) Src=<test.opt:14:11>)
						)
						Src=<test.opt:14:5>
					)
					(List
						Items=(Slice (ListAny) (ListAny))
						Src=<test.opt:15:5>
					)
					(List
						Items=(Slice
							(Func Name=Op Args=(Slice) Src=<test.opt:16:7>)
						)
						Src=<test.opt:16:5>
					)
					(List Items=(Slice) Src=<test.opt:17:5>)
				)
				Src=<test.opt:11:1>
			)
			Replace=(Func
				Name=Op
				Args=(Slice
					(List
						Items=(Slice
							(Func Name=Op Args=(Slice) Src=<test.opt:21:7>)
							(Func Name=Op Args=(Slice) Src=<test.opt:21:12>)
						)
						Src=<test.opt:21:5>
					)
					(List
						Items=(Slice
							(Func Name=Op Args=(Slice) Src=<test.opt:22:7>)
						)
						Src=<test.opt:22:5>
					)
					(List Items=(Slice) Src=<test.opt:23:5>)
					(CustomFunc
						Name=Func
						Args=(Slice
							(List
								Items=(Slice
									(Func Name=Op Args=(Slice) Src=<test.opt:24:13>)
									(Func Name=Op Args=(Slice) Src=<test.opt:24:18>)
								)
								Src=<test.opt:24:11>
							)
						)
						Src=<test.opt:24:5>
					)
				)
				Src=<test.opt:20:1>
			)
			Src=<test.opt:10:1>
		)
	)
)

#
# Bind expressions in all parts of rule.
#
compile
define Op {
    Input Expr
}

[Binding]
(Op $matchOp:(Op (Func $matchArg:"foo")))
=>
(Op $replaceOp:(Op (Func $replaceArg:"foo")))
----
(Compiled
	(Defines
		(Define
			Comments=(Comments)
			Tags=(Tags)
			Name="Op"
			Fields=(DefineFields
				(DefineField Name="Input" Type="Expr" Src=<test.opt:2:5>)
			)
			Src=<test.opt:1:1>
		)
	)
	(Rules
		(Rule
			Comments=(Comments)
			Name="Binding"
			Tags=(Tags)
			Match=(Func
				Name=Op
				Args=(Slice
					(Bind
						Label="matchOp"
						Target=(Func
							Name=Op
							Args=(Slice
								(CustomFunc
									Name=Func
									Args=(Slice
										(Bind Label="matchArg" Target="foo" Src=<test.opt:6:24>)
									)
									Src=<test.opt:6:18>
								)
							)
							Src=<test.opt:6:14>
						)
						Src=<test.opt:6:5>
					)
				)
				Src=<test.opt:6:1>
			)
			Replace=(Func
				Name=Op
				Args=(Slice
					(Bind
						Label="replaceOp"
						Target=(Func
							Name=Op
							Args=(Slice
								(CustomFunc
									Name=Func
									Args=(Slice
										(Bind Label="replaceArg" Target="foo" Src=<test.opt:8:26>)
									)
									Src=<test.opt:8:20>
								)
							)
							Src=<test.opt:8:16>
						)
						Src=<test.opt:8:5>
					)
				)
				Src=<test.opt:8:1>
			)
			Src=<test.opt:5:1>
		)
	)
)

#
# Compile errors.
#
compile
[Tag]
define Op {
    Input Expr
}
define Op {
    Input1 Expr
    Input2 Expr
    Input3 Expr
}

[UnrecognizedName]
(Unknown) => (Unknown)

[TooManyOpNameArgs]
(Op) => ((OpName "foo" "bar"))

[InvalidOpNameArg]
(Op) => ((OpName "foo"))

[DuplicateLabel]
(Op $input:"foo" $input:"bar") => (Op)

[DuplicateLabel2]
(Op $input:"foo") => (Op $input:"bar")

[UnrecognizedLabel]
(Op $input:* & (Func $unknown)) => (Op)

[DuplicateName]
(Op) => (Op)

[DuplicateName]
(Op) => (Op)

[MatchRef]
(Op $ref) => (Op)

[CustomMultiNames]
(Op (Func | Func2)) => (Op)

[CustomList]
(Op (Func [])) => (Op)

[CustomList2]
(Op (Func [ ... (SubFunc) ... ])) => (Op)

[CustomBool]
(Op (Func (SubFunc) & (SubFunc))) => (Op)

[CustomWildcard]
(Op (Func *)) => (Op)

[OpInCustom]
(Op (Func (Op))) => (Op)

[ConstructTag]
(Op) => (Tag)

[MatchLiteralName]
(Op (Op $bind:Op)) => (Op Op (Func Op))

[IllegalLiteralName]
(Op) => (Op Op (Func Unknown))

[IllegalLiteralName2]
(Op) => (Op Op (Func Op) Unknown)

[DynamicMatchName]
((Op)) => (Op)

[DynamicMatchName2]
(Op ((Op))) => (Op)

[MultipleCustomFuncNames]
(Op (Func | Func2)) => (Op)

[MultipleCustomFuncNames2]
(Op) => (Op (Func | Func2))

[ListMatcher]
(Op [ ... (SubFunc) ... (SubFunc) ]) => (Op)

[ListConstructor]
(Op) => (Op [ (SubFunc) ... ])

[ConstructorMultipleNames]
(Op) => (Op | Op)

[NotEnoughMatchFields]
(Op * * * *) => (Op)
----
test.opt:5:1: duplicate 'Op' define statement
test.opt:12:1: unrecognized match name 'Unknown'
test.opt:15:10: too many arguments to OpName function
test.opt:18:10: invalid OpName argument: argument must be a variable reference
test.opt:21:18: duplicate bind label 'input'
test.opt:24:26: duplicate bind label 'input'
test.opt:27:22: unrecognized variable name 'unknown'
test.opt:32:1: duplicate rule name 'DuplicateName'
test.opt:36:5: match pattern cannot use variable references
test.opt:39:5: custom function cannot have multiple names
test.opt:42:11: custom match function cannot use lists
test.opt:45:11: custom match function cannot use lists
test.opt:48:11: custom match function cannot use boolean expressions
test.opt:51:5: custom match function cannot use wildcard matcher
test.opt:54:11: custom function name cannot be an operator name
test.opt:57:9: construct name cannot be a tag
test.opt:60:5: cannot match literal name 'Op'
test.opt:63:16: Unknown is not an operator name
test.opt:66:9: Unknown is not an operator name
test.opt:69:1: cannot match dynamic name
test.opt:72:5: cannot match dynamic name
test.opt:75:5: custom function cannot have multiple names
test.opt:78:13: custom function cannot have multiple names
test.opt:81:25: list matcher cannot contain multiple expressions
test.opt:84:13: list constructor cannot use '...'
test.opt:87:9: constructor cannot have multiple names
test.opt:90:1: Op has only 3 fields
