
SWYM.Parse = function(tokenlist)
{
	SWYM.tokenlist = tokenlist;
	SWYM.tokenIdx = 0;
	SWYM.curToken = SWYM.tokenlist[0];
	
	var parsetree = SWYM.ParseLevel(0);
    return SWYM.ParseTreePostProcess(parsetree);
}

//=============================================================

SWYM.NextToken = function(step)
{
	SWYM.tokenIdx+=(step?step:1);
	SWYM.curToken = SWYM.tokenlist[SWYM.tokenIdx];
}

//=============================================================

SWYM.PeekNextToken = function()
{
	return SWYM.tokenlist[SWYM.tokenIdx+1];
}

//=============================================================

SWYM.ParseLevel = function(minpriority, startingLhs, parsingHyphen)
{
	var curLhs = startingLhs;
	var curOp = undefined;
	
	var HandleAddOp = function(newOp, isImplicit)
	{
		var newOpNeedsLhs = !newOp.behaviour.takeBrackets && !newOp.behaviour.prefix && !newOp.behaviour.standalone;
		var newOpCanHaveLhs = newOp.behaviour.postfix ||
							(newOp.behaviour.takeBrackets && !newOp.followsNewline) ||
							(newOp.behaviour.infix && (!newOp.behaviour.prefix || !newOp.followsNewline));

		if ( curOp )
		{
			// we're getting two operators in a row: which should be the infix one?
			var curPrecedence = curOp.behaviour.precedence;
			if(curOp.behaviour.prefixprecedence && !curLhs)
				curPrecedence = curOp.behaviour.prefixprecedence;
			
			if( curOp.etc === "etc" )
			{
				var curOpNeedsRhs = false;
				var curOpCanHaveRhs = false;
			}
			else if( curOp.behaviour.takeBrackets )
			{
				var curOpNeedsRhs = false;
				var curOpCanHaveRhs = true;
			}
			else
			{
				var curOpNeedsRhs = curLhs? (!curOp.behaviour.postfix): (!curOp.behaviour.standalone);
				var curOpCanHaveRhs = curLhs? curOp.behaviour.infix: curOp.behaviour.prefix;
			}
			
			var canAddAsRhs = !newOpNeedsLhs && curOpCanHaveRhs;
			
			if ( !curOpCanHaveRhs && !newOpCanHaveLhs )
			{
                SWYM.LogError(newOp.pos, "Expected: operator between '"+curOp.text+" and "+newOp.text+"'");
                if ( !isImplicit ) SWYM.NextToken();
                return undefined;
			}
			else if ( curOpNeedsRhs && newOpNeedsLhs )
			{
				// invalid - we can't satisfy both
				if ( curPrecedence > newOp.behaviour.precedence )
					SWYM.LogError(curOp.pos, "Expected: value after operator '"+curOp.text+"'");
				else
					SWYM.LogError(newOp.pos, "Expected: value before operator '"+newOp.text+"'");
				
				if ( !isImplicit ) SWYM.NextToken();
				return undefined;
			}
			else if ( curOpNeedsRhs || !newOpCanHaveLhs ||
					 (curOpCanHaveRhs && !newOpNeedsLhs && curPrecedence < 
					  (newOp.behaviour.prefixprecedence? newOp.behaviour.prefixprecedence: newOp.behaviour.precedence)) )
			{
				// curOp is infix, newOp is prefix
				// (...) curOp (newOp ...)
				curLhs = SWYM.ParseTreeNode(curLhs, curOp, SWYM.ParseLevel(curPrecedence+1));
				curOp = undefined;
				return undefined;
			}
			else
			{
				// curOp is postfix, newOp is infix
				// (... curOp) newOp (...)
				curLhs = SWYM.ParseTreeNode(curLhs, curOp, undefined);
				curOp = undefined;
				//...now fall through into the "no operator" section.
			}
		}

		// we don't have an operator, so this is adding one
		if ( curLhs && !newOpCanHaveLhs )
		{
			// have to auto-insert the ; operator.
			var result = HandleAddOp( SWYM.NewToken("op", SWYM.curToken.sourcePos, ";"), true );
			if ( result )
			{
				return result;
			}
		}
		else if ( curLhs && newOp.behaviour.precedence < minpriority )
		{
			// the precedence is too low to handle here, throw it to the outer layer
			return {value:curLhs};
		}
		else
		{
            //finally, register the new operator.
			curOp = newOp;
			if ( !isImplicit ) SWYM.NextToken();
            
            if ( curOp.behaviour.takeBrackets )
            {
                curLhs = curLhs? [curLhs]: [];
                
                if( curOp.type === "(fn)" && curLhs.length === 0)
                {
                    // a '.x' style function call with no left-hand side gets a free "it".
                    curLhs.push(SWYM.NewToken("name", curOp.pos, "it"));
                }
            }

			var newOpCanHaveRhs = curOp.behaviour.takeBrackets || (curLhs? curOp.behaviour.infix: curOp.behaviour.prefix);
			if ( !newOpCanHaveRhs )
			{
				// this op is finished, might as well resolve it now
				curLhs = SWYM.ParseTreeNode(curLhs, curOp, undefined);
				curOp = undefined;
			}

			return undefined;
		}
	}
	
    var HandleBracket = function()
    {
        var openBracketToken = SWYM.curToken;
				
        // recurse to handle the contents
        SWYM.NextToken();
        if ( openBracketToken.text === "hyphen" )
            var result = SWYM.ParseLevel(200, undefined, true);
        else
            var result = SWYM.ParseLevel(0);

        if (openBracketToken.text !== "hyphen" && (!SWYM.curToken || SWYM.curToken.type !== "closebracket"))
        {
            SWYM.LogError(openBracketToken.sourcePos, "Unmatched open bracket");
        }
        else if ( openBracketToken.text === "hyphen" )
        {
            // don't chomp the close bracket (since there isn't one).
        }
        else
        {
            if ( openBracketToken.text === "(" && SWYM.curToken.text === ")" )
            {
                // nothing special
                result = SWYM.ParseTreeNode(result, SWYM.NewToken("op", openBracketToken.sourcePos, "(parentheses())"));
            }
            else if ( openBracketToken.text === "{" && SWYM.curToken.text === "}" )
            {
                result = SWYM.ParseTreeNode(result, SWYM.NewToken("op", openBracketToken.sourcePos, "(lambda{})"));
            }
            else if ( openBracketToken.text === "[" && SWYM.curToken.text === "]" )
            {
                result = SWYM.ParseTreeNode(result, SWYM.NewToken("op", openBracketToken.sourcePos, "(merge[])"));
            }
            else
            {
                SWYM.LogError(SWYM.curToken.sourcePos, "Mismatched brackets "+openBracketToken.text+SWYM.curToken.text);
            }
            // ok, brackets parsed successfully. Chomp the close bracket and continue
            SWYM.NextToken();
        }
        return result;
    }
    
	var HandleAddLeaf = function()
	{
		if ( !curLhs && !curOp )
		{
			// haven't defined anything yet, start with the lhs
			var nexttoken = SWYM.PeekNextToken();
			if ( SWYM.curToken.type === "name" && nexttoken && nexttoken.type === "openbracket" )
			{
                // it's a fn() style function call. We'll fix it up into ().fn style.
                curOp = SWYM.NewToken("(fn)", SWYM.curToken.sourcePos, "."+SWYM.curToken.text);

                // copy any extra information out of the old token
                curOp.numToken = SWYM.curToken.numToken;
                curOp.startsWithHash = SWYM.curToken.startsWithHash;
                curLhs = [];

                SWYM.NextToken();
			}
			else if ( SWYM.curToken.type === "openbracket" )
			{
                curLhs = HandleBracket();
                if ( parsingHyphen )
                {
                    return {value:curLhs};
                }
			}
			else
			{
				curLhs = SWYM.curToken;
				SWYM.NextToken();
			}
		}
        else if ( curOp && curOp.behaviour.takeBrackets && SWYM.curToken.type === "openbracket" )
        {
            curLhs.push(HandleBracket());
            if ( parsingHyphen )
            {
                return {value:SWYM.ParseTreeNode(curLhs, curOp)};
            }
        }
        else if ( !curOp || curOp.behaviour.takeBrackets )
        {
            // no operator provided, so insert a semicolon here.
			var result = HandleAddOp( SWYM.NewToken("op", SWYM.curToken.sourcePos, ";"), true );
			if ( result ) { return result; }

			// then try adding this leaf again
			result = HandleAddLeaf();
			if ( result ) { return result; }
		}
        else if ( curOp.behaviour.takeBrackets && SWYM.curToken.type === "openbracket" )
        {
            curLhs.push(HandleBracket());
            if ( parsingHyphen )
            {
                return {value:SWYM.ParseTreeNode(curLhs, curOp)};
            }
        }
		else 
		{
			// we have an operator (and maybe an lhs). Recursively get the rhs, then apply the operator to it.
			if( curOp.behaviour.prefixprecedence && !curLhs )
				curLhs = SWYM.ParseTreeNode(curLhs, curOp, SWYM.ParseLevel(curOp.behaviour.prefixprecedence+1));
			else
				curLhs = SWYM.ParseTreeNode(curLhs, curOp, SWYM.ParseLevel(curOp.behaviour.precedence+1));
			curOp = undefined;
		}
	}


    // the actual body of SWYM.ParseLevel
	do
	{
		if ( !SWYM.curToken || SWYM.curToken.type === "closebracket" )
		{
			// end of file, or close bracket (which is treated much the same)
			return SWYM.ParseTreeNode(curLhs, curOp, undefined);
		}
		else if ( SWYM.curToken.type === "name" && (SWYM.curToken.text === "etc" || SWYM.curToken.text === "etc.." ||
				SWYM.curToken.text === "etc../" || SWYM.curToken.text === "etc..<" || SWYM.curToken.text === "etc..>" ||
				SWYM.curToken.text === "etc..<=" || SWYM.curToken.text === "etc..>="))
		{
			if( !curOp )
			{
				SWYM.LogError(SWYM.curToken.pos, "Expected an operator before 'etc'.");
				return undefined;
			}

			curOp.etc = SWYM.curToken.text;
			
			SWYM.NextToken();
		}
		else if ( SWYM.curToken.behaviour )
		{
			// adding an operator ( or operator equivalent such as .foo or foo: )
			var result = HandleAddOp( SWYM.curToken );
			if ( result )
			{
				return result.value;
			}
		}
		else
		{
			// adding a leaf
			var result = HandleAddLeaf( );
			if ( result )
			{
				return result.value;
			}
		}
	}
	while (true);
}

//=============================================================

SWYM.ParseTreeNode = function(lhs, op, rhs)
{
    if ( lhs && !op && !rhs )
    {
        return lhs;
    }
    else if ( !lhs && !op && rhs )
    {
        return rhs;
    }
    else if ( !lhs && !op && !rhs )
    {
        return undefined;
    }
    else
    {
        var children;
        if( op.behaviour.takeBrackets )
        {
            children = lhs;
            if( rhs ) children.push(rhs);
        }
        else if ( rhs )
        {
            children = [lhs,rhs];
        }
        else if ( lhs )
        {
            children = [lhs];
        }
        else
        {
            children = [];
        }

        return {
            children:children,
            op:op,
            toString:function(){ return "(" + (this.children[0]?this.children[0]+" ":"") + this.op.text + (this.children[1]?" "+this.children[1]:"") + ")"; },
            type:"node"
        };
    }
}

//=============================================================

SWYM.ParseTreePostProcess = function(parsetree)
{
    if ( !parsetree )
        return undefined;
        
    if ( parsetree.type !== "node" )
        return parsetree;

    if ( !parsetree.op || !parsetree.children )
        return undefined;
    
    var firstChild = parsetree.children[0];

 //   // convert ':x = 4' into '4:x'
//    if ( parsetree.op.text === "=" &&
//        firstChild && firstChild.op && firstChild.op.type === "(decl)" &&
//        (firstChild.children.length === 0 || (firstChild.children.length === 1 && firstChild.children[0].implicit)) )
//    {
//        firstChild.children.push(parsetree.children[1]);
//        return SWYM.ParseTreePostProcess(firstChild);
//    }
    
    // a function with no args (not even a pair of brackets) gets a free 'it'
//    if( parsetree.op.type === "(fn)" && !firstChild )
  //  {
      //  parsetree.children.push(SWYM.NewToken("name", parsetree.op.pos, "it"));
    //}

    // handle x.1st style functions by appending the number to it as the last parameter: x.nth(1)
    // NB this must come after the free 'it' we added above
    if ( parsetree.op.numToken )
    {
        parsetree.children.push(parsetree.op.numToken);
        parsetree.op.numToken = undefined;
    }

    // strip off unnecessary information in function declarations - just leave parameter names     
/*    if ( parsetree.op.type === "(decl)" && parsetree.children.length > 1)
    {
        // it's a function definition
        var processedBrackets = [];
        // the last bracket will be the function body, stop before it
        for( var Idx = 0; Idx < parsetree.children.length-1; Idx++ )
        {
            var processed = SWYM.ParseTreePostProcess(parsetree.children[Idx]);
            while ( processed && processed.op && processed.op.text === "(parentheses())" )
            {
                processed = processed.children[0];
            }
            if ( processed && processed.op && processed.op.type === "(decl)" )
            {
                processedBrackets.push(processed.op.text);
            }
            else
            {
                SWYM.LogError(processed? processed.pos: 0, "Expected: declaration");
                processedBrackets.push(null);
            }
        }
         
        // now handle the last bracket, i.e. the body
        var body = SWYM.ParseTreePostProcess(parsetree.children[Idx]);
        if ( body && body.op && body.op.text === "(lambda{})" )
        {
            parsetree.op.body = body.children[0];
        }
        else if ( body && body.type === "name" && body.text[0] === "." )
        {
            parsetree.op.body = body;
        }
        else
        {
            SWYM.LogError(parsetree.op.pos, "Expected: function body");
        }

        if ( processedBrackets.length > 0 )
            parsetree.op.brackets = processedBrackets;
        else
            parsetree.op.brackets = undefined;
        
        parsetree.children = [];
        return parsetree;
    }
*/    
    // convert {.foo} into identifier ".foo"
//    if ( parsetree.op.type === "lambda{}" && firstChild && firstChild.type === "node" &&
//            firstChild.op.type === "(fn)" && firstChild.children.length === 0 && !firstChild.op.numToken )
//    {
//        var resultToken = SWYM.NewToken("name", firstChild.op.pos, firstChild.op.text);
//        return resultToken;
//    }

    for( var Idx = 0; Idx < parsetree.children.length; Idx++ )
    {
        var processed = SWYM.ParseTreePostProcess(parsetree.children[Idx]);
        parsetree.children[Idx] = processed;
    }
	
	return parsetree;
}

//=============================================================
/*	for( var n = 0; n < tokenlist.length; n++ )
	{
		var token = tokenlist[n];
		
		// Forms (other than operators):
		// <whatever> [nospc] fn  - function call
		// name ( <whatever> ) - function call
		// <whatever> decl - declaring a variable or constant
		// <whatever> fndecl () {} - declaring a function
		// <whatever> type fndecl () {} - declaring a function
		// <whatever> <whatever> - category intersect
		
		// basically we have (postfix) declarations, (postfix) member-style functions, and 2 (infix) pseudo-operators:
		// foo() style function call, and intersect.
		
		switch(token.type)
		{
			case 'op':
				
				break;
			case 'bracket':
				var endbracket;
				switch( token.text )
				{
					case '(': endbracket = ')';
					case '{': endbracket = '}';
					case '[': endbracket = ']';
					default:
						LogError(token.sourcePos, "Unexpected bracket type "+token);
						endbracket = ')';
				}
				var bracketbody = SWYM.Parse(tokenlist.slice(n+1), endbracket);
				break;
			default:
		}
	}
	return tokenlist.slice(1,2);
}*/
