// SiteswapParser.jj
//
// Copyright 2004 by Jack Boyce (jboyce@users.sourceforge.net) and others

/*
    This file is part of Juggling Lab.

    Juggling Lab is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Juggling Lab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Juggling Lab; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
This is a parser grammar file for use with the parser generator JavaCC
(https://javacc.dev.java.net/)

Process this file using JavaCC ("javacc SiteswapParser.jj") to generate
the following source files:

SiteswapParser.java
SiteswapParserConstants.java
SiteswapParserTokenManager.java
ParseException.java
SimpleCharStream.java
Token.java
TokenMgrError.java

It is wise to avoid directly editing any of the source files above, as
JavaCC will overwrite any changes if this file is processed again later.
*/

options {
	STATIC = false;
}

PARSER_BEGIN(SiteswapParser)

package jugglinglab.notation;

// This file is part of Juggling Lab.  It was automatically generated by JavaCC
// from the grammar file "SiteswapParser.jj".
//
// DO NOT EDIT THIS FILE DIRECTLY, AS YOUR CHANGES WILL LIKELY BE OVERWRITTEN

public class SiteswapParser {
	private static int jugglers;
	private static int current_juggler;
	private static int current_beat;
	private static int current_beat_sub;
	
	public static SiteswapTreeItem parsePattern(String pat) throws ParseException {
		java.io.StringReader sr = new java.io.StringReader(pat);
		java.io.Reader r = new java.io.BufferedReader(sr);
		SiteswapParser parser = new SiteswapParser(r);
		jugglers = -1;
		return (parser.MakeTree());
	}

	// For test purposes
	public static void main(String args[]) throws ParseException {
		String in = args[0];
		System.out.println("parsing pattern: "+in);
		System.out.println(parsePattern(in));
	}
}

PARSER_END(SiteswapParser)

SKIP :
{ "\t" | "\n" | "\r" }

TOKEN :
{
	<NUM: ["0"-"9"]>
|
	<LET: ["a"-"o","q"-"w","y","z"]>
|
	<MOD: (["A"-"K","M"-"Q","S"-"Z"])+>
|
	<SPC: ([" "])+>
}


SiteswapTreeItem MakeTree() :
{ SiteswapTreeItem b; }
{
	[<SPC>] b=Pattern() <EOF>
	{ return b; }
}

SiteswapTreeItem Pattern() :
{ SiteswapTreeItem b, c; int n; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_PATTERN); }
	(
		(
			LOOKAHEAD(GroupedPattern())
			c=GroupedPattern()
		|
			c=SoloSequence()
		|
			c=PassingSequence()
		|
			n=Wildcard()
				{
					c = new SiteswapTreeItem(SiteswapTreeItem.TYPE_WILDCARD);
					c.beats = n;
				}
		)
		{ b.addChild(c); }
	)+
	["*"
		{ b.switchrepeat = true; }
	]

	{
		b.jugglers = jugglers;
		return b;
	}
}

SiteswapTreeItem GroupedPattern() :
{ SiteswapTreeItem b, c; int n; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_GROUPED_PATTERN); }	
	"(" c=Pattern()
		{
			b.addChild(c);
			b.repeats = 1;
		}
	["^" n=Number()
		{ b.repeats = n; }
	] ")"
	{ return b; }
}


SiteswapTreeItem SoloSequence() :
{ SiteswapTreeItem b, c; Token s=null; }
{
	{
		b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_SOLO_SEQUENCE);
		current_juggler = 1;
		current_beat = 0;
	}	
	(
		LOOKAHEAD(SoloMultiThrow() | SoloPairedThrow() | SoloHandSpecifier())
		(
			c=SoloMultiThrow()
				{
					current_beat++;
					b.addChild(c);
				}
		|
			c=SoloPairedThrow()
				{ current_beat += 2; }
			["!"
				{ current_beat--; }
			]
				{ b.addChild(c); }
			[<SPC>]
		|
			c=SoloHandSpecifier()
			[<SPC>]
				{ b.addChild(c); }
		)
	)+
	{
		if (jugglers == -1)
			jugglers = 1;
		else if (jugglers != 1) {
			throw new ParseException("Inconsistent number of jugglers");
		}
		b.source_juggler = 1;
		b.beats = current_beat;
		return b;
	}
}

SiteswapTreeItem SoloPairedThrow() :
{ SiteswapTreeItem b, c; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_SOLO_PAIRED_THROW); }	
	"(" [<SPC>] c=SoloMultiThrow()
		{ b.addChild(c); }
	"," [<SPC>] c=SoloMultiThrow()
		{ b.addChild(c); }
	")"
	{
		b.source_juggler = 1;
		b.seq_beatnum = current_beat;
		return b;
	}
}

SiteswapTreeItem SoloMultiThrow() :
{ SiteswapTreeItem b, c; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_SOLO_MULTI_THROW); }	
	(
		c=SoloSingleThrow()
			{ b.addChild(c); }
		[<SPC>]
	|
		"[" [<SPC>]
		(
			c=SoloSingleThrow()
				{ b.addChild(c); }
		[<SPC>]
		)+
		"]" [<SPC>]
	|
		"{" [<SPC>]
		(
			c=SoloSingleThrow()
				{ b.addChild(c); }
		[<SPC>]
		)+
		"}" [<SPC>]
	)
	{
		b.source_juggler = 1;
		b.seq_beatnum = current_beat;
		return b;
	}
}

SiteswapTreeItem SoloSingleThrow() :
{ SiteswapTreeItem b; Token t; String mod; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_SOLO_SINGLE_THROW); }
	(
		(
			t=<NUM>
				{ b.value = Character.digit((t.image.toCharArray())[0], 36); }
		|
			t=<LET> 
				{ b.value = Character.digit((t.image.toCharArray())[0], 36); }
		|
			"p"
				{ b.value = 25; }
		|
			"x"
				{ b.value = 33; }
		)
		
		["x"
			{ b.x = true; }
		] [mod=Modifier()
			{ b.mod = mod; }
		] ["/"]
	)
	{
		b.source_juggler = 1;
		b.dest_juggler = 1;
		b.seq_beatnum = current_beat;
		return b;
	}
}

SiteswapTreeItem SoloHandSpecifier() :
{ SiteswapTreeItem b; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_HAND_SPEC); }	
	(
		"R"
			{ b.spec_left = false; }
	|
		"L"
			{ b.spec_left = true; }
	)
	{
		b.source_juggler = 1;
		b.seq_beatnum = current_beat;
		return b;
	}
}



SiteswapTreeItem PassingSequence() :
{ SiteswapTreeItem b, c; }
{
	{
		b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_PASSING_SEQUENCE);
		current_beat = 0;
	}
	(
		c=PassingGroup()
			{
				b.jugglers = c.jugglers;
				b.addChild(c);
			}
	)+
	{
		b.beats = current_beat;
		return b;
	}
}


SiteswapTreeItem PassingGroup() :
{ SiteswapTreeItem b, c; }
{
	{
		b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_PASSING_GROUP);
		current_juggler = 1;
	}
	"<" [<SPC>] c=PassingThrows()
		{
			b.beats = c.beats;
			b.addChild(c);
			current_juggler++;
		}
	(
		"|" [<SPC>] c=PassingThrows()
			{
				if (c.beats != b.beats) {
					throw new ParseException("Inconsistent number of beats between jugglers");
				}
				b.addChild(c);
				current_juggler++;
			}
	)*
	">" [<SPC>]
	{
		b.jugglers = current_juggler - 1;
		if (jugglers == -1)
			jugglers = b.jugglers;
		else if (b.jugglers != jugglers) {
			throw new ParseException("Inconsistent number of jugglers");
		}
		b.seq_beatnum = current_beat;
		current_beat += b.beats;
		return b;
	}
}


SiteswapTreeItem PassingThrows() :
{ SiteswapTreeItem b, c; }
{
	{
		b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_PASSING_THROWS);
		current_beat_sub = 0;
	}
	(
		LOOKAHEAD(PassingMultiThrow() | PassingPairedThrow() | PassingHandSpecifier())
		(
			c=PassingMultiThrow()
				{
					current_beat_sub++;
					b.addChild(c);
				}
		|
			c=PassingPairedThrow()
				{ current_beat_sub += 2; }
			["!"
				{ current_beat_sub--; }
			]
			[<SPC>]
				{ b.addChild(c); }
		|
			c=PassingHandSpecifier()
			[<SPC>]
				{ b.addChild(c); }
		)
	)+
	{
		b.source_juggler = current_juggler;
		b.beats = current_beat_sub;
		b.seq_beatnum = current_beat;
		return b;
	}
}

SiteswapTreeItem PassingPairedThrow() :
{ SiteswapTreeItem b, c; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_PASSING_PAIRED_THROW); }
	"(" [<SPC>] c=PassingMultiThrow()
		{ b.addChild(c); }
	"," [<SPC>] c=PassingMultiThrow()
		{ b.addChild(c); }
	")"
	{
		b.source_juggler = current_juggler;
		b.seq_beatnum = current_beat + current_beat_sub;
		return b;
	}
}

SiteswapTreeItem PassingMultiThrow() :
{ SiteswapTreeItem b, c; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_PASSING_MULTI_THROW); }
	(
		c=PassingSingleThrow()
			{ b.addChild(c); }
		[<SPC>]
	|
		"[" [<SPC>]
		(
			c=PassingSingleThrow()
				{ b.addChild(c); }
			[<SPC>]
		)+
		"]" [<SPC>]
	|
		"{" [<SPC>]
		(
			c=PassingSingleThrow()
				{ b.addChild(c); }
			[<SPC>]
		)+
		"}" [<SPC>]
	)
	{
		b.source_juggler = current_juggler;
		b.seq_beatnum = current_beat + current_beat_sub;
		return b;
	}
}

SiteswapTreeItem PassingSingleThrow() :
{ SiteswapTreeItem b, c; Token t; int dest, d; String mod; }
{
	{
		b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_PASSING_SINGLE_THROW);
		dest = current_juggler;
	}
	(
		(
			t=<NUM>
				{ b.value = Character.digit((t.image.toCharArray())[0], 36); }
		|
			t=<LET> 
				{ b.value = Character.digit((t.image.toCharArray())[0], 36); }
		|
			"p"
				{ b.value = 25; }
		|
			"x"
				{ b.value = 33; }
		)
		
		["x"
			{ b.x = true; }
		] ["p"
			{ dest = current_juggler + 1; }
		[d=Number()
			{ dest = d; }
		]] [mod=Modifier()
			{ b.mod = mod; }
		] ["/"]
	)
	{
		b.source_juggler = current_juggler;
		b.dest_juggler = dest;
		b.seq_beatnum = current_beat + current_beat_sub;
		return b;
	}
}

SiteswapTreeItem PassingHandSpecifier() :
{ SiteswapTreeItem b; }
{
	{ b = new SiteswapTreeItem(SiteswapTreeItem.TYPE_HAND_SPEC); }	
	(
		"R"
			{ b.spec_left = false; }
	|
		"L"
			{ b.spec_left = true; }
	)
	{
		b.source_juggler = current_juggler;
		b.seq_beatnum = current_beat + current_beat_sub;
		return b;
	}
}


int Number() :
{ Token n; String s = ""; }
{
	(
		n=<NUM>
			{ s += n.image; }
	)+
	{
		int val = -1;
		try {
			val = Integer.parseInt(s);
		} catch (NumberFormatException mfe) {
		}
		return val;
	}
}


int Wildcard() :
{ int beats = 0; }
{
	( "?"
		{ beats++; }
	)+
	{ return beats; }
}


String Modifier() :
{ Token t; String mod = ""; }
{
	t=<MOD>
		{ mod += t.image; }
	(
		t=<MOD>
			{ mod += t.image; }
	|
		"L"
			{ mod += "L"; }
	|
		"R"
			{ mod += "R"; }
	)*
	{ return mod; }
}
