/*

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Some notes about this file.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Bison is a LALR bottom-up parser, it is quite old technology and can be hard to use.
LALR is quite difficult to write compared to more modern approaches, but it will produce reduce-shift conflicts if you make a mistake. 
There should be *no* warnings when generating the final parser.

Bottom-up parsing means that the parts of the rule are parsed before the rule itself. All the child nodes are produced *before* the parent. 
This is in stark contrast to other types of parsing, like recursive descent.

This grammar was made in the early 2000s and is showing its age. 
For example, there is a lot of duplication here, particularly regarding the 'msgsend' rule.
This has grown naturally as the language has evolved and could use a refactor, however, changing this requires some form of fuzz testing to ensure compatibility is maintained.
While taking previously invalid syntax and making it valid is generally okay, refactoring existing syntax must be thoroughly tested.

If you are looking at documentation on Bison you will often see it accompanied with Flex, that part is not relevant to understanding this file — although it can be useful.


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Some notes relationship between the lexer and the parser.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Bison uses global variables everywhere, again, this is because of its age.
We do not support having multiple instances of the parser at once (this is a feature in modern Bison), therefore no multithreading.

The lexer's job is to define a function called yylex.

It produces an int, which is an enum (weakly typed) of the type yytokentype, which can be found in the generated header.
yytokentype, being backed by an integer, can contain all utf8 characters, along with all the special %tokens declared below.

Additionally, yylex writes to several globals used to communicate with the parser.

yylval:

This is a union of type YYSTYPE and contains any produced data.
For example, if tokenizing a double, the lexer could turn the text into a double and write the result there.
YYSTYPE is defined below with the %union statement.

yylloc:

This is the location in the source file of the token. 


It is recommended to read a basic introduction to bison before proceeding, but for ease of understanding:

yyval is accessed in each rule by using '$N' and $$, where 'N' refers to the index of the child rule, and '$$' is the produced value.
Note, '$$' is the field of the union, not the union itself, Bison will automatically deduce which field you should be writing to by looking at the %type statement for that rule.

yyloc is accessed in each by using @$ and @N. Most of the time, this isn't necessary as bison will automatically figure out the correct location info for you.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Some notes on memory
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

All PyrParseNodes are created using an arena allocator. 
We do not use bison's destructor feature.
You don't have to worry about memory here in this file, but when using the parser there is additional setup and tear-down required.

*/

// This is added to the header.
%code requires { 
#include "BisonHeaderInclude.hpp"
}

// This union will become YYSTYPE. 
// It is what each rule and token produces. 
// Which field is active is determined by the %type <field> statement or %token <field>. 
// The lexer can write to this through yylval, and the parser (this file) can access this by reading from $N and writing to $$.

%union { 
	struct PyrParseNode* node;  // Base class.
	struct PyrSlotNode* slotNode; // For some literal value
	struct PyrVarListNode* varListNode;
	struct PyrVarDefNode* varDefNode;
	struct PyrMethodNode* methodNode;
	struct PyrArgListNode* argListNode;
	struct PyrBlockNode* blockNode;
	struct PyrMultiAssignVarListNode* multiAssignListNode;
	struct PyrRootNode* root;
	ReadWriteAccessor rwAccessor;
	EmptyYYVal empty;
}

// Enable yyloc and the use of @N to access the source code location struct.
%locations
%define api.location.type {sc::lex::SourceCodeRange}

%token <empty> OPENCURLY CLOSECURLY OPENSQUARE CLOSESQUARE OPENPAREN CLOSEPAREN 
%token <empty> SEMICOLON NONLOCALRETURN COMMA HASH TILDE
%token <slotNode> NAME INTEGER SC_FLOAT ACCIDENTAL SYMBOL STRING ASCII PRIMITIVENAME CLASSNAME CURRYARG 
%token <empty> VAR ARG CLASSVAR SC_CONST
%token <slotNode> NILOBJ TRUEOBJ FALSEOBJ PIE
%token <empty> ELLIPSIS DOTDOT BEGINCLOSEDFUNC
%token <empty> BADTOKEN INTERPRET
%token <slotNode> LEFTARROW WHILE

%left <empty> COLON
%right <empty> EQUALSSIGN
%left <slotNode> BINOP KEYBINOP MINUS LESSTHAN GREATERTHAN MULTIPLY ADD PIPE READWRITEVAR
%left <empty> DOT
%right <slotNode> BACKTICK
%right <empty> UMINUS


%type <rwAccessor> rspec rwspec 

%type <slotNode> floatp floatr integer accidental name binop binop2 optName optSuperName optPrim nameList

%type <varListNode> classvardecls classvardecl funcvardecls funcvardecl funcvardecls1

%type <varDefNode> rwslotdef rwslotdeflist constdeflist constdef vardef vardeflist vardeflist0 slotdef slotdeflist slotdeflist0

%type <methodNode> methoddef methods

%type <argListNode> argdecls argdecls1

%type <multiAssignListNode> mavars

%type <root> root

%type <node> listlit2 literallistc listlit classes classextensions classextension 
%type <node> classdef generator qual nextqual expr expr1 exprn exprseq arrayelems arrayelems1 arglist1 arglistv1 keyarglist1 
%type <blockNode> cmdlinecode
%type <node> keyarg optkeyarglist valrangex1 valrangeassign valrangexd literallist1 funcbody funretval pushliteral
%type <node> blockliteral pushname msgsend adverb dictslotlist retval blocklist1 blocklist valrange2 valrange3
%type <node> slotliteral listliteral block methbody dictslotdef dictslotlist1 blocklistitem curryArg

%start root

%{

#include <stdlib.h>
#include <string.h>
#include "PyrLexer.h"
#include "PyrParseNode.h"
#include "SC_Constants.h"
#include "PredefinedSymbols.h"
#include "SimpleStack.h"


void yyerror(const char* s) { bison_cxt->logErrorInCurFile(yylloc, s); }

inline bool checkArraySubrangeSyntax(PyrParseNode* args, const size_t argLen) {
	if(argLen <= 2) 
		return true;
	const auto first = args->mNext->mNext;
	auto last = first;
	while(true) {
		auto next = last->mNext;
		if (next) {
			last = next;
		} else 
			break;
	}
	const auto range = sc::lex::SourceCodeRange::range(first->location, last->location);
	bison_cxt->postErrorInCurrentFile(range, "Invalid subrange", "expected 2 or less arguments, delete these.");
	return false;
}

%}

%define parse.error verbose

%%

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Begin parser rules
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


root	: classes
			{ 
				$$ = bison_cxt->allocParseNode<PyrRootNode>(@$, $1);
				bison_cxt->assignRoot(*$$);
			}
		| classextensions
			{ 
				$$ = bison_cxt->allocParseNode<PyrRootNode>(@$, $1);
				bison_cxt->assignRoot(*$$);
			}
		| INTERPRET cmdlinecode
			{ 
				$$ = bison_cxt->allocParseNode<PyrRootNode>(@$, $2);
				bison_cxt->assignRoot(*$$);
			};

classes : %empty { $$ = nullptr; }
		| classes classdef 
			{ $$ = linkNodes($1, $2); };

classextensions : classextension
				| classextensions classextension
					{ $$ = linkNodes($1, $2); };

classdef	 : CLASSNAME optSuperName OPENCURLY classvardecls methods CLOSECURLY
				{ $$ = bison_cxt->allocParseNode<PyrClassNode>(@$, $1, $2, nullptr, $4, $5); }
			| CLASSNAME OPENSQUARE optName CLOSESQUARE optSuperName OPENCURLY classvardecls methods CLOSECURLY
				{ $$ = bison_cxt->allocParseNode<PyrClassNode>(@$, $1, $5, $3, $7, $8); };

classextension : ADD CLASSNAME OPENCURLY methods CLOSECURLY
					{ $$ = bison_cxt->allocParseNode<PyrClassExtNode>(@$, $2, $4); };

optName		: %empty { $$ = nullptr; } | name;

// Note the inclusion of the colon here.
optSuperName	: %empty { $$ = nullptr; } | COLON CLASSNAME { $$ = $2; };

classvardecls	: %empty { $$ = nullptr; }
				| classvardecls classvardecl
					{ $$ = linkNodes($1, $2); };

classvardecl	: CLASSVAR rwslotdeflist SEMICOLON
					{ $$ = bison_cxt->allocParseNode<PyrVarListNode>(@$, $2, varClass); }
				| VAR rwslotdeflist SEMICOLON
					{ $$ = bison_cxt->allocParseNode<PyrVarListNode>(@$, $2, varInst); }
				| SC_CONST constdeflist SEMICOLON
					{ $$ = bison_cxt->allocParseNode<PyrVarListNode>(@$, $2, varConst); };

methods		: %empty { $$ = nullptr; }
				| methods methoddef
					{ $$ = linkNodes($1, $2); };

methoddef	: name OPENCURLY argdecls funcvardecls optPrim methbody CLOSECURLY
				{ $$ = bison_cxt->allocParseNode<PyrMethodNode>(@$, $1, $5, $3, $4, $6, false); }
			| MULTIPLY name OPENCURLY argdecls funcvardecls optPrim methbody CLOSECURLY
				{ $$ = bison_cxt->allocParseNode<PyrMethodNode>(@$, $2, $6, $4, $5, $7, true); }
			| binop OPENCURLY argdecls funcvardecls optPrim methbody CLOSECURLY
				{ $$ = bison_cxt->allocParseNode<PyrMethodNode>(@$, $1, $5, $3, $4, $6, false); }
			| MULTIPLY binop OPENCURLY argdecls funcvardecls optPrim methbody CLOSECURLY
				{ $$ = bison_cxt->allocParseNode<PyrMethodNode>(@$, $2, $6, $4, $5, $7, true); };

optsemi		: %empty | SEMICOLON;

optcomma	: %empty | COMMA;

optequal	: %empty | EQUALSSIGN;

funcbody	: funretval
			| exprseq funretval
				{ $$ = bison_cxt->allocParseNode<PyrDropNode>(@$, $1, $2); };

cmdlinecode	: OPENPAREN argdecls1 funcvardecls1 funcbody CLOSEPAREN
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, $2, $3, $4, false); }
			| OPENPAREN argdecls1 funcbody CLOSEPAREN
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, $2, nullptr, $3, false); }
			| OPENPAREN funcvardecls1 funcbody CLOSEPAREN
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, nullptr, $2, $3, false); }
			| argdecls1 funcvardecls1 funcbody
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, $1, $2, $3, false); }
			| argdecls1 funcbody
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, $1, nullptr, $2, false); }
			| funcvardecls1 funcbody
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, nullptr, $1, $2, false); }
			| funcbody
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, nullptr, nullptr, $1, false); };

methbody	: retval
			| exprseq retval
				{ $$ = bison_cxt->allocParseNode<PyrDropNode>(@$, $1, $2); };

optPrim	: %empty  { $$ = nullptr; }
		| PRIMITIVENAME optsemi { $$ = $1; };

retval	: %empty { $$ = bison_cxt->allocParseNode<PyrReturnNode>(@$, nullptr); }
		| NONLOCALRETURN expr optsemi
			{ $$ = bison_cxt->allocParseNode<PyrReturnNode>(@$, $2); };

funretval	: %empty
				{ $$ = bison_cxt->allocParseNode<PyrBlockReturnNode>(@$, nullptr); }
			| NONLOCALRETURN expr optsemi
				{ $$ = bison_cxt->allocParseNode<PyrReturnNode>(@$, $2); };

blocklist1	: blocklistitem
			| blocklist1 blocklistitem
				{ $$ = linkNodes($1, $2); };

blocklistitem : blockliteral | generator;

blocklist	: %empty { $$ = nullptr; } | blocklist1;

msgsend : name blocklist1
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $1, $2, nullptr); }
			
		| OPENPAREN binop2 CLOSEPAREN blocklist1
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $2, $4, nullptr); }

		| name OPENPAREN CLOSEPAREN blocklist1
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $1, $4, nullptr); }

		| name OPENPAREN arglist1 optkeyarglist CLOSEPAREN blocklist
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $1, linkNodes($3, $6), $4); }

		| OPENPAREN binop2 CLOSEPAREN OPENPAREN CLOSEPAREN blocklist1
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $2, $6, nullptr); }

		| OPENPAREN binop2 CLOSEPAREN OPENPAREN arglist1 optkeyarglist CLOSEPAREN blocklist
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $2, linkNodes($5, $8), $6); }

		| name OPENPAREN arglistv1 optkeyarglist CLOSEPAREN
			{
				PyrSlot selector;
				// Is the keyword 'super' in 'msg(super, *[])'.
				if (isSuperObjNode($3)) {
					auto* slot_node = nodeCast<PyrSlotNode>($3);
					slot_node->mSlot = PyrSlot::make(s_this);
					selector = PyrSlot::make(s_superPerformList);
				} else {
					selector = PyrSlot::make(s_performList);
				}
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, selector);
				auto* args = linkAfterHead($3, $1->changeLiteralType<PyrParseNodeType::PushLitNode>());
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, $4);
			}
		| OPENPAREN binop2 CLOSEPAREN OPENPAREN arglistv1 optkeyarglist CLOSEPAREN
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_performList));
				auto* args = linkAfterHead($5, $2->changeLiteralType<PyrParseNodeType::PushLitNode>());
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, $6);
			}
		| CLASSNAME OPENSQUARE arrayelems CLOSESQUARE
			{ $$ = bison_cxt->allocParseNode<PyrDynListNode>(@$, $1, $3); }

		| CLASSNAME blocklist1
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_new));
				auto* args = linkNodes($1->changeLiteralType<PyrParseNodeType::PushNameNode>(), $2);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| CLASSNAME OPENPAREN CLOSEPAREN blocklist
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_new));
				auto* args = linkNodes($1->changeLiteralType<PyrParseNodeType::PushNameNode>(), $4);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| CLASSNAME OPENPAREN keyarglist1 optcomma CLOSEPAREN blocklist
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_new));
				auto* args = linkNodes($1->changeLiteralType<PyrParseNodeType::PushNameNode>(), $6);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, $3);
			}
		| CLASSNAME OPENPAREN arglist1 optkeyarglist CLOSEPAREN blocklist
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_new));
				auto* args = linkNodes(
					$1->changeLiteralType<PyrParseNodeType::PushNameNode>(), 
					$3,
					$6);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, $4);
			}
		| CLASSNAME OPENPAREN arglistv1 optkeyarglist CLOSEPAREN
			{
				PyrSlot selector;
				if (isSuperObjNode($1)) {
					// Ehh? this is not possible. CLASSNAME cannot be 'super'.
					$1->mSlot = PyrSlot::make(s_this);
					selector = PyrSlot::make(s_superPerformList);
				} else {
					selector = PyrSlot::make(s_performList);
				}

				auto* new_selector_push_lit = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_new), PyrParseNodeType::PushLitNode);
				auto* args = linkNodes(
					$1->changeLiteralType<PyrParseNodeType::PushNameNode>(),
					new_selector_push_lit,
					$3
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, selector);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, $4);
			}
		| expr DOT OPENPAREN CLOSEPAREN blocklist
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_value));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, $5), nullptr);
			}
		| expr DOT OPENPAREN keyarglist1 optcomma CLOSEPAREN blocklist
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_value));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, $7), $4);
			}
		| expr DOT name OPENPAREN keyarglist1 optcomma CLOSEPAREN blocklist
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $3, linkNodes($1, $8), $5); }
		| expr DOT OPENPAREN arglist1 optkeyarglist CLOSEPAREN blocklist
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_value));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, $4, $7), $5);
			}
		| expr DOT OPENPAREN arglistv1 optkeyarglist CLOSEPAREN
			{
				PyrSlot selectorSlot;
				if (isSuperObjNode($1)) {
					nodeCast<PyrSlotNode>($1)->mSlot = PyrSlot::make(s_this);
					selectorSlot = PyrSlot::make(s_superPerformList);
				} else {
					selectorSlot = PyrSlot::make(s_performList);
				}
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, selectorSlot);
				auto* args = linkNodes(
					$1, 
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_value), PyrParseNodeType::PushLitNode),
					$4
				);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, $5);
			}
		| expr DOT name OPENPAREN CLOSEPAREN blocklist
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $3, linkNodes($1, $6), nullptr); }
		| expr DOT name OPENPAREN arglist1 optkeyarglist CLOSEPAREN blocklist
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $3, linkNodes($1, $5, $8), $6); }
		| expr DOT name OPENPAREN arglistv1 optkeyarglist CLOSEPAREN
			{
				PyrSlot slot;
				if (isSuperObjNode($1)) {
					nodeCast<PyrSlotNode>($1)->mSlot = PyrSlot::make(s_this);
					slot = PyrSlot::make(s_superPerformList);
				} else {
					slot = PyrSlot::make(s_performList);
				}
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, slot);
				auto* args = linkNodes($1, $3->changeLiteralType<PyrParseNodeType::PushLitNode>(), $5);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, $6);
			}
		| expr DOT name blocklist
			{ $$ = bison_cxt->allocParseNode<PyrCallNode>(@$, $3, linkNodes($1, $4), nullptr); } ;



// This section is for list comprehension/generators. 
// It uses the generatorStack and I do not know how it works. 
// In future, it would be good to create some documentation for this.
// For now, the code is left as it is.
generator : OPENCURLY COLON exprseq { bison_cxt->pushToGenerator($3, 1); } COMMA qual CLOSECURLY
			{
				PyrSlotNode* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("r")));
				PyrParseNode *block = bison_cxt->allocParseNode<PyrBlockNode>(@$, nullptr, nullptr, $6, false);
				PyrParseNode *blocklit = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>(block)), PyrParseNodeType::PushLitNode);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, blocklit, nullptr);
			}
		| OPENCURLY SEMICOLON exprseq { bison_cxt->pushToGenerator($3, 2); } COMMA qual CLOSECURLY
			{ $$ = $6; };

nextqual	: %empty
				{
					// innermost part
					const auto [expr, action] = bison_cxt->popFromGenerator();
					if (action == 1) 
						$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("yield"))), expr, nullptr);
					else if (action == 2)
						$$ = expr; 
					// if action is neither, then what?
				}
			| COMMA qual 
				{ $$ = $2; };

qual	: name LEFTARROW exprseq nextqual
			{
				// later should check if exprseq is a series and optimize it to for loop
				
				auto* var = bison_cxt->allocParseNode<PyrVarDefNode>(@1, $1, nullptr, ReadWriteAccessor::Private);
				auto* args = bison_cxt->allocParseNode<PyrArgListNode>(@1, var, nullptr, nullptr);
				auto *block = bison_cxt->allocParseNode<PyrBlockNode>(@1, args, nullptr, $4, false);
				auto *blocklit = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>(block)), PyrParseNodeType::PushLitNode);
				if (auto* call = nodeCast<PyrCallNode>($3); call && call->mSelector->mSlot.getSymbol() == s_series) {
						call->mSelector->mSlot = PyrSlot::make(getsym("forSeries"));
						call->mArglist = linkNodes(call->mArglist, blocklit);
						$$ = call;
				} else {
					auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@3, PyrSlot::make(getsym("do")));
					$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($3, blocklit), nullptr);
				}
			}
		| name name LEFTARROW exprseq nextqual
			{
				auto* call = nodeCast<PyrCallNode>($4);
				const auto is_series = call ? (call->mSelector->mSlot.getSymbol() == s_series) : false;

				auto* var1 = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $1, nullptr, ReadWriteAccessor::Private);
				auto* var2 = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $2, nullptr, ReadWriteAccessor::Private);
				auto* args = bison_cxt->allocParseNode<PyrArgListNode>(@$, linkNodes(var1, var2), nullptr, nullptr);
				auto *block = bison_cxt->allocParseNode<PyrBlockNode>(@$, args, nullptr, $5, false);
				auto *blocklit = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>(block)), PyrParseNodeType::PushLitNode);

				if (auto* call = nodeCast<PyrCallNode>($3); call && call->mSelector->mSlot.getSymbol() == s_series) {
					call->mSelector->mSlot = PyrSlot::make(getsym("forSeries"));
					call->mArglist = linkNodes(call->mArglist, blocklit);
					$$ = call;
				} else {
					auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@3, PyrSlot::make(getsym("do")));
					$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($4, blocklit), nullptr);
				}
			}
		| VAR name EQUALSSIGN exprseq nextqual
			{
				PyrSlot slot = PyrSlot::make(s_value);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, slot);
				auto* var = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $2, nullptr, ReadWriteAccessor::Private);
				auto* args = bison_cxt->allocParseNode<PyrArgListNode>(@$, var, nullptr, nullptr);
				auto *block = bison_cxt->allocParseNode<PyrBlockNode>(@$, args, nullptr, $5, false);
				auto *blocklit = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>(block)), PyrParseNodeType::PushLitNode);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes(blocklit, $4), nullptr);
			}
		| exprseq nextqual
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("if")));
				auto* block = bison_cxt->allocParseNode<PyrBlockNode>(@$, nullptr, nullptr, $2, false);
				auto* blocklit = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>(block)), PyrParseNodeType::PushLitNode);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, blocklit), nullptr);
			}
		| COLON COLON exprseq nextqual
			{ $$ = bison_cxt->allocParseNode<PyrDropNode>(@$, $3, $4); }
		| COLON WHILE exprseq nextqual
			{
				auto* selectornode1 = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("alwaysYield")));
				auto* pushnil = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode);
				auto* yieldNil = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode1, pushnil, nullptr);
				auto* block1 = bison_cxt->allocParseNode<PyrBlockNode>(@$, nullptr, nullptr, yieldNil, false);
				auto* blocklit1 = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>(block1)), PyrParseNodeType::PushLitNode);
				auto* block2 = bison_cxt->allocParseNode<PyrBlockNode>(@$, nullptr, nullptr, $4, false);
				auto* blocklit2 = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>(block2)), PyrParseNodeType::PushLitNode);

				auto* selectornode2 = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("if")));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode2, linkNodes($3, blocklit2, blocklit1), nullptr);
			};

expr1	: pushliteral
		| blockliteral
		| generator
		| pushname
		| curryArg
		| msgsend
		| OPENPAREN exprseq CLOSEPAREN
			{
				PyrParseNode* node = $2;
				node->mParens = true; // This means  (((((1))))), will not count the brackets, but just indicates they exist.
				node->location = @$; // make the location include the brackets.
				$$ = $2;
			}
		| TILDE name
			{
				auto* argnode = $2->changeLiteralType<PyrParseNodeType::PushLitNode>();
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_envirGet));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, argnode, nullptr);
			}
		| OPENSQUARE arrayelems CLOSESQUARE
			{ $$ = bison_cxt->allocParseNode<PyrDynListNode>(@$, nullptr, $2); }
		| OPENPAREN valrange2 CLOSEPAREN
			{ $$ = $2; }
		|	OPENPAREN COLON valrange3 CLOSEPAREN
			{ $$ = $3; }
		|	OPENPAREN dictslotlist CLOSEPAREN
			{ $$ = bison_cxt->allocParseNode<PyrDynDictNode>(@$, $2); }
		| expr1 OPENSQUARE arglist1 CLOSESQUARE
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_at));
				auto* args = linkNodes($1, $3);
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| valrangex1;

valrangex1	: expr1 OPENSQUARE arglist1 DOTDOT CLOSESQUARE
			{
				const auto arglen = nodeListLength($3);
				checkArraySubrangeSyntax($3, arglen);
				auto* args = linkNodes($1, $3);
				if (arglen < 2) 
					args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));

				args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_copyseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr1 OPENSQUARE DOTDOT exprseq CLOSESQUARE
			{
				auto* args = linkNodes(
					$1, 
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode)
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode)
					$4
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_copyseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr1 OPENSQUARE arglist1 DOTDOT exprseq CLOSESQUARE
			{
				const int arglen = nodeListLength($3);
				checkArraySubrangeSyntax($3, arglen);

				auto* args = linkNodes($1, $3);
				if (arglen < 2) 
					args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));
				
				args = linkNodes(args, $5);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_copyseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			};

valrangeassign : expr1 OPENSQUARE arglist1 DOTDOT CLOSESQUARE EQUALSSIGN expr
			{
				const int arglen = nodeListLength($3);
				checkArraySubrangeSyntax($3, arglen);
				
				auto* args = linkNodes($1, $3);
				if (arglen < 2) 
					args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));
				
				args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode), $7);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_putseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr1 OPENSQUARE DOTDOT exprseq CLOSESQUARE EQUALSSIGN expr
			{
				auto* args = linkNodes(
					$1,
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode),
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode),
					$4, 
					$7
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_putseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr1 OPENSQUARE arglist1 DOTDOT exprseq CLOSESQUARE EQUALSSIGN expr
			{
				const int arglen = nodeListLength($3);
				auto* args = linkNodes($1, $3);
				if (arglen < 2) 
					args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));
				
				args = linkNodes(args, $5, $8);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_putseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			};


// This is a complete duplicate of valrange1, but contains an extra dot.
// However, this is a part of expr, and valrange1 a part of expr1.
// This probably has some weird consequences!
// If it is refactored in the future, make sure nothing unexpected happens.
valrangexd	: expr DOT OPENSQUARE arglist1 DOTDOT CLOSESQUARE
			{
				PyrSlotNode* nilnode1, *nilnode2;
				PyrSlot selectorSlot, nilSlot;
				PyrParseNode* args;

				const int arglen = nodeListLength($4);

				checkArraySubrangeSyntax($4, arglen);

				args = linkNodes($1, $4);
				if (arglen < 2) 
					args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, nilSlot, PyrParseNodeType::PushLitNode));

				args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));

				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_copyseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr DOT OPENSQUARE DOTDOT exprseq CLOSESQUARE
			{
				auto* args = linkNodes(
					$1,
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode),
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode),
					$5
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_copyseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr DOT OPENSQUARE arglist1 DOTDOT exprseq CLOSESQUARE
			{
				const int arglen = nodeListLength($4);

				checkArraySubrangeSyntax($4, arglen);

				auto* args = linkNodes($1, $4);
				if (arglen < 2) 
					args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));
				
				args = linkNodes(args, $6);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_copyseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr DOT OPENSQUARE arglist1 DOTDOT CLOSESQUARE EQUALSSIGN expr
			{
				const int arglen = nodeListLength($4);
				checkArraySubrangeSyntax($4, arglen);
				auto* args = linkNodes($1, $4);
				
				if (arglen < 2) 
					args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));
					
				args = linkNodes(
					args, 
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode), 
					$8);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_putseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr DOT OPENSQUARE DOTDOT exprseq CLOSESQUARE EQUALSSIGN expr
			{
				auto* args = linkNodes(
					$1, 
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode),
					bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode), 
					$5, 
					$8);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_putseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr DOT OPENSQUARE arglist1 DOTDOT exprseq CLOSESQUARE EQUALSSIGN expr
			{
				const int arglen = nodeListLength($4);
				checkArraySubrangeSyntax($4, arglen);

				auto* args = linkNodes($1, $4);
				if (arglen < 2) 
					args = linkNodes(args, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));
				
				args = linkNodes(args, $6, $9);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_putseries));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			};

valrange2	: exprseq DOTDOT
			{
				// if this is not used in a 'do' or list comprehension, then should return an error.
				auto* args = linkNodes(
					$1, 
					bison_cxt->allocParseNode<PyrSlotNode>(@2, PyrSlot{}, PyrParseNodeType::PushLitNode)
					bison_cxt->allocParseNode<PyrSlotNode>(@2, PyrSlot{}, PyrParseNodeType::PushLitNode)
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_series));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| DOTDOT exprseq
			{
				auto* args = linkNodes(
					bison_cxt->allocParseNode<PyrSlotNode>(@1, PyrSlot::make(0), PyrParseNodeType::PushLitNode),
					bison_cxt->allocParseNode<PyrSlotNode>(@1, PyrSlot{}, PyrParseNodeType::PushLitNode),
					$2
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_series));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| exprseq DOTDOT exprseq
			{
				auto* args = linkNodes($1, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode), $3);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_series));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| exprseq COMMA exprseq DOTDOT exprseq
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_series));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, $3, $5), nullptr);
			}
		| exprseq COMMA exprseq DOTDOT
			{
				// if this is not used in a 'do' or list comprehension, then should return an error.
				auto* args = linkNodes($1, $3, bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot{}, PyrParseNodeType::PushLitNode));
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_series));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			};

valrange3	: DOTDOT exprseq
			{
				auto* args = linkNodes(
					bison_cxt->allocParseNode<PyrSlotNode>(@1, PyrSlot::make(0), PyrParseNodeType::PushLitNode),
					bison_cxt->allocParseNode<PyrSlotNode>(@1, PyrSlot{}, PyrParseNodeType::PushLitNode),
					$2
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("seriesIter")));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| exprseq DOTDOT
			{
				auto* args = linkNodes(
					$1,
					bison_cxt->allocParseNode<PyrSlotNode>(@1, PyrSlot{}, PyrParseNodeType::PushLitNode),
					bison_cxt->allocParseNode<PyrSlotNode>(@1, PyrSlot{}, PyrParseNodeType::PushLitNode)
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("seriesIter")));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}

		| exprseq DOTDOT exprseq
			{
				auto* args = linkNodes(
					$1,
					bison_cxt->allocParseNode<PyrSlotNode>(@2, PyrSlot{}, PyrParseNodeType::PushLitNode),
					$3
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("seriesIter")));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| exprseq COMMA exprseq DOTDOT
			{
				auto* args = linkNodes(
					$1,
					$3,
					bison_cxt->allocParseNode<PyrSlotNode>(@2, PyrSlot{}, PyrParseNodeType::PushLitNode)
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("seriesIter")));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| exprseq COMMA exprseq DOTDOT exprseq
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(getsym("seriesIter")));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, $3, $5), nullptr);
			};

expr	: expr1
		| valrangexd
		| valrangeassign
		| CLASSNAME 
			{ $$ = $1->changeLiteralType<PyrParseNodeType::PushNameNode>(); }
		| expr DOT OPENSQUARE arglist1 CLOSESQUARE
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_at));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, $4), nullptr);
			}
		| BACKTICK expr
			{
				auto* args = linkNodes(
					bison_cxt->allocParseNode<PyrSlotNode>(@1, PyrSlot::make(s_ref), PyrParseNodeType::PushNameNode),
					$2
				);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_new));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr binop2 adverb expr %prec BINOP
			{
				$1->mNext = $4;
				$4->mNext = $3;
				$$ = bison_cxt->allocParseNode<PyrBinopCallNode>(@$, $2, $1);
			}
		| name EQUALSSIGN expr
			{ $$ = bison_cxt->allocParseNode<PyrAssignNode>(@$, $1, $3); }
		| TILDE name EQUALSSIGN expr
			{
				auto* args = linkNodes($2->changeLiteralType<PyrParseNodeType::PushLitNode>(), $4);
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_envirPut));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, args, nullptr);
			}
		| expr DOT name EQUALSSIGN expr
			{ $$ = bison_cxt->allocParseNode<PyrSetterNode>(@$, $3, $1, $5); }

		// TODO: since optkeyarglist has to be nullptr, find a way of removing it without introducing shift/reduce conflists.
		| name OPENPAREN arglist1 optkeyarglist CLOSEPAREN EQUALSSIGN expr
			{
				if ($4 != nullptr) {
					bison_cxt->postErrorInCurrentFile($4->location, "Setter method call with keyword arguments", "delete these keyword arguments.");
				}
				$$ = bison_cxt->allocParseNode<PyrSetterNode>(@$, $1, $3, $7);
			}
		| HASH mavars EQUALSSIGN expr
			{ $$ = bison_cxt->allocParseNode<PyrMultiAssignNode>(@$, $2, $4); }
		| expr1 OPENSQUARE arglist1 CLOSESQUARE EQUALSSIGN expr
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_put));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, $3, $6), nullptr);
			}
		| expr DOT OPENSQUARE arglist1 CLOSESQUARE EQUALSSIGN expr
			{
				auto* selectornode = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(s_put));
				$$ = bison_cxt->allocParseNode<PyrCallNode>(@$, selectornode, linkNodes($1, $4, $7), nullptr);
			};

adverb  : %empty  { $$ = nullptr; }
		| DOT name { $$ = $2->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
		| DOT integer { $$ = $2->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
		| DOT OPENPAREN exprseq CLOSEPAREN { $$ = $3; };

exprn	: expr
		| exprn SEMICOLON expr
			{ $$ = bison_cxt->allocParseNode<PyrDropNode>(@$, $1, $3); } ;

exprseq : exprn optsemi;

arrayelems	: %empty { $$ = nullptr; }
			| arrayelems1 optcomma { $$ = $1; } ;

arrayelems1	: exprseq
			| exprseq COLON exprseq
				{ $$ = linkNodes($1, $3); }
			| KEYBINOP exprseq
				{ $$ = linkNodes( $1->changeLiteralType<PyrParseNodeType::PushLitNode>(), $2); }
			| arrayelems1 COMMA exprseq
					{ $$ = linkNodes($1, $3); }
			| arrayelems1 COMMA KEYBINOP exprseq
				{ $$ = linkNodes($1, $3->changeLiteralType<PyrParseNodeType::PushLitNode>(), $4); }
			| arrayelems1 COMMA exprseq COLON exprseq
				{ $$ = linkNodes($1, $3, $5); };

arglist1	: exprseq
			| arglist1 COMMA exprseq
				{ $$ = linkNodes($1, $3); };

arglistv1	: MULTIPLY exprseq
				{
					@2 = @$; // replace text location with just the exprseq
					$$ = $2;
				}
			| arglist1 COMMA MULTIPLY exprseq
					{ $$ = linkNodes($1, $4); };

keyarglist1	: keyarg
			| keyarglist1 COMMA keyarg
					{ $$ = linkNodes($1, $3); };

keyarg	: KEYBINOP exprseq
			{ $$ = bison_cxt->allocParseNode<PyrPushKeyArgNode>(@$, $1, $2); };

optkeyarglist	: optcomma { $$ = nullptr; }
				| COMMA keyarglist1 optcomma { $$ = $2; };

mavars	: nameList
			{ $$ = bison_cxt->allocParseNode<PyrMultiAssignVarListNode>(@$, $1, nullptr); }
		| nameList ELLIPSIS name
			{ $$ = bison_cxt->allocParseNode<PyrMultiAssignVarListNode>(@$, $1, $3); };

nameList: name
		| nameList COMMA name
			{ $$ = linkNodes($1, $3); };

slotliteral	
		: integer	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
		| floatp	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
		| ASCII		{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
		| STRING	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
		| SYMBOL	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
		| TRUEOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
		| FALSEOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
		| NILOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
		| listlit	{ $$ = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>($1)), PyrParseNodeType::LiteralNode); };

blockliteral : block { $$ = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>($1)), PyrParseNodeType::PushLitNode); };

pushname	: name { $$ = $1->changeLiteralType<PyrParseNodeType::PushNameNode>(); };

pushliteral	: integer	{ $$ = $1->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
			| floatp	{ $$ = $1->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
			| ASCII		{ $$ = $1->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
			| STRING	{ $$ = $1->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
			| SYMBOL	{ $$ = $1->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
			| TRUEOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
			| FALSEOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
			| NILOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::PushLitNode>(); }
			| listlit	{ $$ = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>($1)), PyrParseNodeType::PushLitNode); };

listliteral	: integer	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>();}
			| floatp	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
			| ASCII		{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
			| STRING	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
			| SYMBOL	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
			| name		{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
			| TRUEOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
			| FALSEOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
			| NILOBJ	{ $$ = $1->changeLiteralType<PyrParseNodeType::LiteralNode>(); }
			| listlit2	{ $$ = bison_cxt->allocParseNode<PyrSlotNode>(@$, PyrSlot::make(static_cast<void*>($1)), PyrParseNodeType::LiteralNode); }

block	: OPENCURLY argdecls funcvardecls funcbody CLOSECURLY 
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, $2, $3, $4, false); }
		| BEGINCLOSEDFUNC argdecls funcvardecls funcbody CLOSECURLY 
				{ $$ = bison_cxt->allocParseNode<PyrBlockNode>(@$, $2, $3, $4, true); };

funcvardecls	: %empty { $$ = nullptr; }
				| funcvardecls funcvardecl
					{ $$ = linkNodes($1, $2); };

funcvardecls1	: funcvardecl
				| funcvardecls1 funcvardecl
					{ $$ = linkNodes($1, $2); };

funcvardecl	: VAR vardeflist SEMICOLON
				{ $$ = bison_cxt->allocParseNode<PyrVarListNode>(@$, $2, varLocal); };

argdecls	: %empty { $$ = nullptr; }
			| ARG vardeflist SEMICOLON
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, nullptr, nullptr); }
			| ARG vardeflist0 ELLIPSIS name SEMICOLON
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, $4, nullptr); }
			| ARG vardeflist0 ELLIPSIS name COMMA name SEMICOLON
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, $4, $6); }
			| PIPE slotdeflist PIPE
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, nullptr, nullptr); }
			| PIPE slotdeflist0 ELLIPSIS name PIPE
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, $4, nullptr); }
			| PIPE slotdeflist0 ELLIPSIS name COMMA name PIPE
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, $4, $6); };

argdecls1	: ARG vardeflist SEMICOLON
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, nullptr, nullptr); }
			| ARG vardeflist0 ELLIPSIS name SEMICOLON
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, $4, nullptr); }
			| ARG vardeflist0 ELLIPSIS name COMMA name SEMICOLON
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, $4, $6); }
			| PIPE slotdeflist PIPE
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, nullptr, nullptr); }
			| PIPE slotdeflist0 ELLIPSIS name PIPE
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, $4, nullptr); }
			| PIPE slotdeflist0 ELLIPSIS name COMMA name PIPE
				{ $$ = bison_cxt->allocParseNode<PyrArgListNode>(@$, $2, $4, $6); };

constdeflist	: constdef
				| constdeflist optcomma constdef
					{ $$ = linkNodes($1, $3); } ;

constdef	: rspec name EQUALSSIGN slotliteral
				{ $$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $2, $4, $1); };

slotdeflist0 	: %empty { $$ = nullptr; }
				| slotdeflist;

slotdeflist	: slotdef
			| slotdeflist optcomma slotdef
				{ $$ = linkNodes($1, $3); };

slotdef		: name
				{ $$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $1, nullptr, ReadWriteAccessor::Private); }
			| name optequal slotliteral
				{ $$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $1, $3, ReadWriteAccessor::Private); }
			| name optequal OPENPAREN exprseq CLOSEPAREN
				{
					PyrParseNode* node = $4;
					node->mParens = 1;
					$$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $1, node, ReadWriteAccessor::Private);
				};

vardeflist0 	: %empty  { $$ = nullptr; }
				| vardeflist;

vardeflist      : vardef
				| vardeflist COMMA vardef
					{ $$ = linkNodes($1, $3); };

vardef          : name
					{ $$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $1, nullptr, ReadWriteAccessor::Private); }
				| name EQUALSSIGN expr
					{ $$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $1, $3, ReadWriteAccessor::Private); }
				| name OPENPAREN exprseq CLOSEPAREN
					{
						PyrParseNode* node = $3;
						node->mParens = 1;
						$$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $1, node, ReadWriteAccessor::Private);
					};

dictslotdef	: exprseq COLON exprseq
				{ $$ = linkNodes($1, $3); }
			| KEYBINOP exprseq
				{ $$ = linkNodes($1->changeLiteralType<PyrParseNodeType::PushLitNode>(), $2); };

dictslotlist1	: dictslotdef
				| dictslotlist1 COMMA dictslotdef
					{ $$ = linkNodes($1, $3); } ;

dictslotlist	: %empty { $$ = nullptr; }
				| dictslotlist1 optcomma ;

rwslotdeflist	: rwslotdef
				| rwslotdeflist COMMA rwslotdef
					{ $$ = linkNodes($1, $3); };

rwslotdef		: rwspec name
					{ $$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $2, nullptr, $1); }
				| rwspec name EQUALSSIGN slotliteral
					{ $$ = bison_cxt->allocParseNode<PyrVarDefNode>(@$, $2, $4, $1); };

listlit	: HASH OPENSQUARE literallistc CLOSESQUARE
				{ $$ = bison_cxt->allocParseNode<PyrLitListNode>(@$, nullptr, $3); }
		| HASH CLASSNAME  OPENSQUARE literallistc CLOSESQUARE
				{ $$ = bison_cxt->allocParseNode<PyrLitListNode>(@$, $2, $4); };

listlit2	: OPENSQUARE literallistc CLOSESQUARE
				{ $$ = bison_cxt->allocParseNode<PyrLitListNode>(@$, nullptr, $2); }
			| CLASSNAME  OPENSQUARE literallistc CLOSESQUARE
				{ $$ = bison_cxt->allocParseNode<PyrLitListNode>(@$, $1, $3); };

literallistc	: %empty { $$ = nullptr; }
				| literallist1 optcomma;

literallist1	: listliteral
				| literallist1 COMMA listliteral
					{ $$ = linkNodes($1, $3); };

rwspec	: %empty { $$ = ReadWriteAccessor::Private; }
		| LESSTHAN { $$ = ReadWriteAccessor::Read; }
		| READWRITEVAR { $$ = ReadWriteAccessor::ReadWrite; }
		| GREATERTHAN { $$ = ReadWriteAccessor::Write; };

rspec	: %empty { $$ = ReadWriteAccessor::Private; } 
		| LESSTHAN { $$ = ReadWriteAccessor::Read; };

integer	: INTEGER 
		| MINUS INTEGER %prec UMINUS 
			{
				const auto v = $2->mSlot.getInt();
				$2->mSlot = PyrSlot::make(-v);
				$$ = $2;
			};

floatr	: SC_FLOAT 
		| MINUS SC_FLOAT %prec UMINUS 
			{
				const double v = $2->mSlot.getDouble();
				$2->mSlot = PyrSlot::make(-v);
				$$ = $2;
			};

accidental : ACCIDENTAL 
			| MINUS ACCIDENTAL %prec UMINUS 
				{
					const double in = $2->mSlot.getDouble();
					const double intval = floor(in + 0.5);
					const double fracval = in - intval;
					$2->mSlot = PyrSlot::make(-intval + fracval);
					$$ = $2;
				};

floatp	: floatr
		| accidental
		| floatr PIE 
			{
				$1->mSlot = PyrSlot::make($1->mSlot.getDouble() * pi);
				$1->location = @$;
				$$ = $1;
			}
		| integer PIE 
			{
				$1->mSlot = PyrSlot::make($1->mSlot.getInt() * pi);
				$1->location = @$;
				$$ = $1;
			}
		| PIE 
		| MINUS PIE 
			{
				$2->mSlot = PyrSlot::make(-pi);
				$2->location = @$;
				$$ = $2;
			};

name 		: NAME 
			| WHILE;

binop	: BINOP 
		| READWRITEVAR 
		| LESSTHAN 
		| GREATERTHAN 
		| MINUS 
		| MULTIPLY 
		| ADD 
		| PIPE;

binop2 	: binop 
		| KEYBINOP;

curryArg : CURRYARG { $$ = bison_cxt->allocParseNode<PyrCurryArgNode>(@$); }
