
# The Imba parser is generated by [Jison](http://github.com/zaach/jison)
# from this grammar file. Jison is a bottom-up parser generator, similar in
# style to [Bison](http://www.gnu.org/software/bison), implemented in JavaScript.
# It can recognize [LALR(1), LR(0), SLR(1), and LR(1)](http://en.wikipedia.org/wiki/LR_grammar)
# type grammars. To create the Jison parser, we list the pattern to match
# on the left-hand side, and the action to take (usually the creation of syntax
# tree nodes) on the right. As the parser runs, it
# shifts tokens from our token stream, from left to right, and
# [attempts to match](http://en.wikipedia.org/wiki/Bottom-up_parsing)
# the token sequence against the rules below. When a match can be made, it
# reduces into the [nonterminal](http://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols)
# (the enclosing name at the top), and we proceed from there.
#
# If you run the `cake build:parser` command, Jison constructs a parse table
# from our rules and saves it into `lib/parser.js`.

# The only dependency is on the **Jison.Parser**.

var jison = require '../../vendor/jison/jison'
var Parser = jison.Parser

# Jison DSL
# ---------

# Since we're going to be wrapped in a function by Jison in any case, if our
# action immediately returns a value, we can optimize by removing the function
# wrapper and just returning the value directly.
var unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/

# Our handy DSL for Jison grammar generation, thanks to
# [Tim Caswell](http://github.com/creationix). For every rule in the grammar,
# we pass the pattern-defining string, the action to run, and extra options,
# optionally. If no action is specified, we simply pass the value of the
# previous nonterminal.

var o = do |patternString, action, options|
	patternString = patternString.replace /\s{2,}/g, ' '
	var patternCount = patternString.split(' '):length

	return [patternString, '$$ = $1;', options] unless action

	if var match = unwrap.exec(action)
		action = match[1]
	else
		action = "({action}())"

	action = action.replace /\bA(\d+)/g, '$$$1'
	action = action.replace /\bnew /g, '$&yy.'
	action = action.replace /\b(?:Block\.wrap|extend)\b/g, 'yy.$&'
	action = action.replace /\bAST\b/g, 'yy'

	# really?
	# # should we always add locdata? does not work when statement)!
	# return [patternString, "$$ = #{loc(1, patternCount)}(#{action});", options]
	[patternString, "$$ = {action};", options]

# Grammatical Rules
# -----------------

# In all of the rules that follow, you'll see the name of the nonterminal as
# the key to a list of alternative matches. With each match's action, the
# dollar-sign variables are provided by Jison as references to the value of
# their numeric position, so in this rule:
#
#     "Expression UNLESS Expression"
#
# `A1` would be the value of the first `Expression`, `A2` would be the token
# for the `UNLESS` terminal, and `A3` would be the value of the second
# `Expression`.
var grammar =

	# The **Root** is the top-level node in the syntax tree. Since we parse bottom-up,
	# all parsing must end here.
	Root: [
		o '' do Root.new([])
		o 'Body' do Root.new(A1)
		o 'Block TERMINATOR'
	]

	# Any list of statements and expressions, separated by line breaks or semicolons.
	Body: [
		o 'BODYSTART' do Block.new([])
		o 'Line' do Block.new([]).add(A1)
		# o 'HEADER Line' do Block.new([A2])
		# o 'LeadingTerminator' do Block.new([Terminator.new(A1)])
		o 'Body Terminator Line' do A1.break(A2).add(A3) # A3.prebreak(A2) # why not add as real nodes?!
		o 'Body Terminator' do A1.break(A2)
	]

	Terminator: [
		o 'TERMINATOR' do Terminator.new(A1)
	]

	Type: [
		o 'TYPE' do TypeAnnotation.new(A1)
		o 'GENERICS' do Generics.new(A1)
	]

	# An indented block of expressions. Note that the [Rewriter](rewriter.html)
	# will convert some postfix forms into blocks for us, by adjusting the
	# token stream.
	Block: [
		o 'EMPTY_BLOCK' do Block.new([])
		o 'INDENT OUTDENT' do Block.new([]).indented(A1,A2)
		o 'INDENT Body OUTDENT' do A2.indented(A1,A3)
		# hacky way to support terminators at the start of blocks
		o 'INDENT TERMINATOR Body OUTDENT' do A3.prebreak(A2).indented(A1,A4)
		o 'INDENT TERMINATOR OUTDENT' do Block.new([]).indented(A1,A3)
	]

	# Block and statements, which make up a line in a body.
	Line: [
		o 'CSSDeclaration'
		o 'Expression'
		# ?
		o 'VarDecl' do A1.option('block',yes)
		o 'Comment'
		o 'Statement'
		o 'Decorators'

		o 'ImportDeclaration'
		o 'ExportDeclaration'
		o 'GLOBAL Line' do AST.GLOBAL(A2,A1)
		o 'DECLARE Line' do AST.DECLARE(A2,A1)
		# A2.set(declareOnly: A1)
	]




	# Pure statements which cannot be expressions.
	Statement: [
		o 'Return'
		o 'Yield'
		o 'Throw'
		o 'STATEMENT' do Literal.new A1

		o 'BREAK' do BreakStatement.new A1
		o 'BREAK CALL_START Expression CALL_END' do BreakStatement.new A1,A3

		o 'CONTINUE' do ContinueStatement.new A1
		o 'CONTINUE CALL_START Expression CALL_END' do ContinueStatement.new A1,A3

		o 'DEBUGGER' do DebuggerStatement.new A1
	]

	ExtendObject: [
		# o 'CLASS ClassName ClassBody' do ClassDeclaration.new(A2, null, A3).set(keyword: A1)
		o 'EXTEND Identifier ClassBody' do
			ExtendDeclaration.new(A2, null, A3).set(instanceOnly: true, extension: A1)
	]

	ExportDeclaration: [
		o 'ENV_FLAG ExportDeclaration' do
			A2.addEnv(A1)
		o 'EXPORT { ImportSpecifierList }' do
			ExportNamedDeclaration.new(A1,[A3]).setEnds(A1,A4)
		o 'EXPORT { ImportSpecifierList } FROM String' do
			ExportNamedDeclaration.new(A1,[A3], A6).setEnds(A1,A6)
		o 'EXPORT EXPORT_ALL FROM String' do
			ExportAllDeclaration.new(A1,[ExportAllSpecifier.new(A2)],A4).setEnds(A1,A4)
		o 'EXPORT EXPORT_ALL AS Identifier FROM String' do
			ExportAllDeclaration.new(A1,[ExportAllSpecifier.new(A2,A4)],A6).setEnds(A1,A6)
		o 'EXPORT Exportable' do
			Export.new(A2).set(keyword: A1).setEnds(A1,A2)
		o 'EXPORT DEFAULT DefaultExportable' do
			Export.new(A3).set(keyword: A1,default: A2).setEnds(A1,A3)
	]

	Exportable: [
		o 'MethodDeclaration'
		o 'Class'
		o 'CSSDeclaration'
		o 'TagDeclaration'
		o 'VarAssign'
		o 'GLOBAL Exportable' do AST.GLOBAL(A2,A1)
	]

	DefaultExportable: [
		o 'Expression'
		o 'GLOBAL Exportable' do AST.GLOBAL(A2,A1)
	]

	ImportOrExport: [
		o 'IMPORT'
		o 'EXPORT'
	]

	ImportDefaultSpecifier: [
		o 'Identifier' do ImportDefaultSpecifier.new(A1)
	]

	ImportDeclaration: [
		o 'ENV_FLAG ImportDeclaration' do
			A2.addEnv(A1)
		o 'IMPORT String' do
			ImportDeclaration.new(A1,null, A2)
		o 'IMPORT ImportDefaultSpecifier FROM String' do
			ImportDeclaration.new(A1,[A2], A4)
		o 'IMPORT TYPEIMPORT ImportDefaultSpecifier FROM String' do
			ImportTypeDeclaration.new(A1,[A3], A5)
		o 'IMPORT ImportNamespaceSpecifier FROM String' do
			ImportDeclaration.new(A1,[A2], A4)
		o 'IMPORT { } FROM String' do
			ImportDeclaration.new(A1,null, A5)
		o 'IMPORT { ImportSpecifierList } FROM String' do
			ImportDeclaration.new(A1,[A3], A6)
		o 'IMPORT TYPEIMPORT { ImportSpecifierList } FROM String' do
			ImportTypeDeclaration.new(A1,[A4], A7)
		o 'IMPORT ImportDefaultSpecifier IMPORT_COMMA ImportNamespaceSpecifier FROM String' do
			ImportDeclaration.new(A1,[A2,A4], A6)
		o 'IMPORT ImportDefaultSpecifier IMPORT_COMMA { ImportSpecifierList } FROM String' do
			ImportDeclaration.new(A1,[A2,A5], A8)
	]

	ImportFrom: [
		o 'STRING'
	]

	ImportNamespaceSpecifier: [
		o 'IMPORT_ALL AS Identifier' do ImportNamespaceSpecifier.new(Literal.new(A1),A3)
	]

	ImportSpecifierList: [
		o 'ImportSpecifier' do ESMSpecifierList.new([]).add(A1)
		o 'ImportSpecifierList , ImportSpecifier' do A1.add(A3)
		o 'ImportSpecifierList OptComma TERMINATOR ImportSpecifier' do A1.add A4
		o 'INDENT ImportSpecifierList OptComma OUTDENT' do A2
		o 'INDENT ImportSpecifierList OptComma TERMINATOR OUTDENT' do A2
		o 'ImportSpecifierList OptComma INDENT ImportSpecifierList OptComma OUTDENT' do A1.concat A4
	]

	ImportSpecifier: [
		o 'Identifier' do ImportSpecifier.new(A1)
		o 'DecoratorIdentifier' do ImportSpecifier.new(A1)
		o 'StyleMixinIdentifier' do ImportSpecifier.new(A1)
		o 'Identifier AS Identifier' do ImportSpecifier.new(A1, A3)
		o 'DEFAULT' do ImportSpecifier.new(Literal.new(A1))
		o 'DEFAULT AS Identifier' do ImportSpecifier.new(Literal.new(A1), A3)
	]

	Require: [
		o 'REQUIRE RequireArg' do Require.new(A2).set(keyword: A1)
	]

	RequireArg: [
		o 'Literal'
		o 'Parenthetical'
		o ''
	]

	# All the different types of expressions in our language. The basic unit of
	# Imba is the **Expression** -- everything that can be an expression
	# is one. Blocks serve as the building blocks of many other rules, making
	# them somewhat circular.
	Expression: [
		o 'Await'
		o 'Value'
		o 'Code'
		o 'Operation'
		o 'Assign'
		o 'If'
		o 'Ternary'
		o 'Try'
		o 'While'
		o 'For'
		o 'Switch'
		o 'ExtendObject'
		o 'Class'
		o 'TagDeclaration'
		o 'Tag'
	]

	ExpressionBlock: [
		o 'Expression'
		o 'INDENT ExpressionBlock Outdent' do A2.indented(A1,A3)
	]

	# A literal identifier, a variable name or property.
	Identifier: [
		o 'IDENTIFIER' do Identifier.new(A1)
	]

	SymbolIdentifier: [
		o 'SYMBOLID' do SymbolIdentifier.new A1
		o '# Interpolation' do InterpolatedSymbolIdentifier.new(A1, A2)
	]

	DecoratorIdentifier: [
		o 'DECORATOR' do DecoratorIdentifier.new A1
	]

	StyleMixinIdentifier: [
		o 'CSS_MIXIN' do MixinIdentifier.new A1
	]

	Key: [
		o 'KEY' do Identifier.new A1
	]

	Argvar: [
		o 'ARGVAR' do Argvar.new(A1).setEnds(A1,A1)
	]

	Symbol: [
		o 'SYMBOL' do Symbol.new A1
	]

	Decorator: [
		o 'DecoratorIdentifier' do Decorator.new A1 # kinda hacky, should be defined as something else
		o 'DecoratorIdentifier Arguments' do Decorator.new(A1).set(params: A2)
		# o 'DecoratorIdentifier ( ArgList )' do Decorator.new(A1).set(params: A3)
		o 'Decorator . Identifier' do A1.add(A3)
	]

	Decorators: [
		o 'Decorator' do [A1]
		o 'Decorators Decorator' do A1.concat(A2)
	]

	# Alphanumerics are separated from the other **Literal** matchers because
	# they can also serve as keys in object literals.
	AlphaNumeric: [
		o 'NUMBER UNIT' do NumWithUnit.new(A1,A2)
		o 'NUMBER' do Num.new A1
		o 'STRING' do Str.new A1
		o 'Symbol'
		o 'InterpolatedString'
	]

	String: [
		o 'STRING' do Str.new A1
	]

	InterpolatedString: [
		o 'STRING_START' do InterpolatedString.new([],open: A1)
		o 'InterpolatedString NEOSTRING' do A1.add A2
		o 'InterpolatedString Interpolation' do A2 ? A1.add(A2) : A1
		o 'InterpolatedString STRING_END' do A1.option('close',A2)
	]

	# The list of arguments to a function call.
	Interpolation: [
		o '{{ }}' do null
		o '{{ Expression }}' do A2
	]

	# All of our immediate values. Generally these can be passed straight
	# through and printed to JavaScript.
	Literal: [
		o 'AlphaNumeric'
		o 'JS' do Literal.new A1
		o 'REGEX' do RegExp.new A1
		o 'BOOL' do Bool.new A1
		o 'TRUE' do True.new(A1) # AST.TRUE # should not cheat like this
		o 'FALSE' do False.new(A1) # AST.FALSE
		o 'NULL' do Nil.new(A1) # AST.NIL
		o 'UNDEFINED' do Undefined.new(A1) # AST.UNDEFINED
		# we loose locations for these
	]

	# A return statement from a function body.
	Return: [
		o 'RETURN Expression' do Return.new(A2).set(keyword: A1)
		o 'RETURN Arguments' do Return.new(A2).set(keyword: A1) # should probably force as array
		o 'RETURN' do Return.new().set(keyword: A1)
	]

	Yield: [
		o 'YIELD Expression' do Yield.new(A2).set(keyword: A1)
		o 'YIELD Arguments' do Yield.new(A2).set(keyword: A1) # should probably force as array
		o 'YIELD' do Yield.new().set(keyword: A1)
	]

	Selector: [
		o 'SELECTOR_START' do Selector.new([],type: A1,open: A1)
		o 'Selector SELECTOR_PART' do A1.add A2
		o 'Selector { Expression }' do A1.add(A3)
		o 'Selector SELECTOR_END' do A1.option('close',A2)
	]

	Tag: [
		o 'TAG_START TagOptions TAG_END' do A2.setEnds(open: A1, close: A3).setEnds(A1,A3)
		o 'TAG_START TagOptions TAG_END TagBody' do A2.set(body: A4, open: A1, close: A3)
	]

	TagTypeName: [
		o 'Self' do A1
		o 'IDENTIFIER' do TagTypeIdentifier.new(A1)
		o 'TAG_TYPE' do TagTypeIdentifier.new(A1)
		o 'TagIdentifier' do ExpressionNode.new(A1)
		o '' do TagTypeIdentifier.new('div')
	]

	StyleBlockDeclaration: [
		o 'CSS CSS_SEL StyleBody CSS_END' do StyleRuleSet.new(A2,A3).set(name: A1)
	]

	CSSDeclaration: [
		# o 'GLOBAL CSS CSS_SEL StyleBody CSS_END' do StyleRuleSet.new(A3,A4).set(global: A1)
		o 'StyleBlockDeclaration' do A1.set(toplevel: true)
		# o 'GLOBAL CSSDeclaration' do A2.set(global: A1)
		o 'LOCAL CSSDeclaration' do A2.set(local: A1)
	]

	StyleBlockBody: [
		o 'INDENT Terminator OUTDENT' do StyleBody.new([]).indented(A1,A3)
		o 'INDENT StyleBody Outdent' do A2.indented(A1,A3)
	]

	OptStyleBody: [
		o '' do StyleBody.new([])
		o 'StyleBody'
	]

	StyleBody: [
		o 'StyleNode' do StyleBody.new([A1])
		o 'StyleBody StyleDeclaration' do A1.add A2
		# o 'Style2Body , Style2Node' do A1.add A3
		o 'StyleBody Terminator StyleNode' do A1.add(A3)
		o 'INDENT StyleBody Outdent' do A2.indented(A1,A3)
	]

	StyleNode: [
		o 'StyleDeclaration'
		o 'CSS_SEL StyleBlockBody CSS_END' do StyleRuleSet.new(A1,A2)
	]

	StyleDeclaration: [
		o 'StyleProperty : StyleExpressions' do StyleDeclaration.new(A1,A3.set(parens: no))
		o 'StyleProperty = StyleExpressions' do StyleDeclaration.new(A1,A3.set(parens: no))
	]

	StyleProperty: [
		o 'CSSPROP' do StyleProperty.new([A1])
	]

	StyleOperator: [
		o 'MATH'
		o '+'
		o '-'
	]

	StyleExpressions: [
		o 'StyleExpression' do StyleExpressions.new([A1])
		o 'StyleExpressions , StyleExpression' do A1.add(A3)
	]

	# the values
	StyleExpression: [
		o 'StyleTerm' do StyleExpression.new().add(A1)
		o 'StyleExpression StyleOperator' do A1.add(A2)
		o 'StyleExpression StyleTerm' do A1.add(A2)
		o 'StyleExpression / StyleTerm' do A1.addParam(A3,A2)
	]

	StyleValue: [
		o 'StyleTerm'
		o 'StyleOperation'
	]

	StyleOperation: [
		o 'StyleTerm StyleOperator StyleTerm' do StyleOperation.new([A1,A2,A3])
		o 'StyleOperation StyleOperator StyleTerm' do A1.add([A2,A3])
	]

	StyleFunctionArgs: [
		o 'StyleFunctionArg' do StyleExpressions.new([A1])
		o 'StyleFunctionArgs , StyleFunctionArg' do A1.add(A3)
	]

	StyleFunctionArg: [
		o 'StyleTerm' do StyleExpression.new().add(A1)
		o 'StyleFunctionArg StyleOperator' do A1.add(A2)
		o 'StyleFunctionArg StyleTerm' do A1.add(A2)
		o 'StyleFunctionArg / StyleTerm' do A1.addParam(A3,A2)
	]

	StyleTermPlaceholder: [
		o '{ Expression }' do StyleInterpolationExpression.new(A2).setEnds(A1,A3)
		o 'StyleTermPlaceholder CSSUNIT' do A1.set(unit: A2)
	]

	StyleParens: [
		o '( StyleValue )' do StyleParens.new(A2).setEnds(A1,A3)
		o 'StyleParens CSSUNIT' do A1.set(unit: A2)
	]

	StyleColor: [
		o 'COLOR' do StyleColor.new(A1)
		o 'COLORMIX ( StyleFunctionArgs )' do StyleColorMix.new(A1,A3.setEnds(A2,A4)).set(params: A3).setEnds(A1,A4)
	]

	StyleTerm: [
		o 'StyleParens'
		o 'CSSVAR' do StyleVar.new(A1)
		o 'DIMENSION' do StyleDimension.new(A1)
		# o 'COLOR ( StyleFunctionArgs )' do StyleColor.new(A1,A3).set(params: A3)
		o 'StyleColor'
		o 'PERCENTAGE' do StyleDimension.new(A1)
		o 'NUMBER' do StyleNumber.new(A1)
		o 'String' do A1
		o 'StyleTermPlaceholder' do A1
		o 'CSSURL' do StyleURL.new(A1)
		o 'CSSFUNCTION ( StyleFunctionArgs )' do StyleFunction.new(A1,A3.setEnds(A2,A4)).setEnds(A1,A4)
		o 'CSSIDENTIFIER' do StyleIdentifier.new(A1)
		o 'COMPARE StyleTerm' do A2.set(op: A1)
	]

	TagOptions: [
		o 'TagTypeName TAG_REF' do Tag.new(type: A1,reference: A2)
		o 'TagTypeName' do Tag.new(type: A1)
		o 'TagOptions TAG_ID' do A1.addPart(A2,AST.TagId)
		o 'TagOptions TAG_SYMBOL_ID' do A1.addPart(IdentifierExpression.new(A2.cloneSlice(1)),AST.TagId)
		o 'TagOptions SYMBOL_ID' do A1.addPart(IdentifierExpression.new(A2.cloneSlice(1)),AST.TagId)
		o 'TagOptions TAG_FLAG' do A1.addPart(A2,AST.TagFlag)
		o 'TagOptions TAG_ATTR' do A1.addPart(A2,AST.TagAttr)
		o 'TagOptions STYLE_START STYLE_END' do A1
		o 'TagOptions STYLE_START StyleBody STYLE_END' do A1.addPart(StyleRuleSet.new(null,A3),AST.TagFlag)
		o 'TagOptions T. STYLE_START StyleBody STYLE_END' do A1.addPart(StyleRuleSet.new(null,A4),AST.TagFlag)
		o 'TagOptions CSS_MIXIN' do A1.addPart(MixinIdentifier.new(A2),AST.TagFlag)
		o 'TagOptions T: TagIdentifier' do A1.addPart(A3,AST.TagHandler)
		o 'TagOptions T@ TagIdentifier' do A1.addPart(A3,AST.TagHandler)
		o 'TagOptions T. @ TAG_LITERAL' do A1.addPart(A4.prepend('_'),AST.TagFlag) # not supported anymore
		o 'TagOptions T. UNARY TAG_LITERAL' do A1.addPart(A4.prepend('!'),AST.TagFlag)
		o 'TagOptions T. TagIdentifier' do A1.addPart(A3,AST.TagFlag)

		o 'TagOptions # TagIdentifier' do A1.addPart(A3,AST.TagId)
		o 'TagOptions TAG_WS TagIdentifier' do A1.addPart(A2,AST.TagSep).addPart(A3,AST.TagAttr)
		o 'TagOptions ( )' do A1.addPart(ArgList.new([]),AST.TagArgList)
		o 'TagOptions ( ArgList )' do A1.addPart(A3,AST.TagArgList)
		o 'TagOptions CALL_START CALL_END' do A1.addPart(null,AST.TagArgList)
		o 'TagOptions CALL_START ArgList CALL_END' do A1.addPart(A3,AST.TagArgList)
		o 'TagOptions TAG_WS' do A1.addPart(A2,AST.TagSep)
		o 'TagOptions Comment' do A1
		o 'TagOptions TERMINATOR' do A1
		# o 'TagOptions TagFlag' do A1.addPart(A2)
		o 'TagOptions = TagAttrValue' do A1.addPart(A3,AST.TagAttrValue,A2)
	]

	TagIdentifier: [
		o 'TAG_LITERAL' do IdentifierExpression.new(A1)
		o '{ Expression }' do IdentifierExpression.new(A2)
		o 'TagIdentifier TAG_LITERAL' do A1.add(A2)
		o 'TagIdentifier { Expression }' do A1.add(A3)
		# o '{ Expression }' do TagIdentifier.new(A2)
	]

	TagFlag: [
		o '%' do TagFlag.new()
		o 'TagFlag TagPartIdentifier' do A1.add(A2)
	]

	TagAttrValue: [
		o 'VALUE_START Expression VALUE_END' do A2
	]

	TagBody: [
		o 'INDENT OUTDENT' do TagBody.new([]).indented(A1,A2)
		o 'INDENT TERMINATOR OUTDENT' do TagBody.new([]).indented(A1,A3)
		o 'INDENT TagBodyList OUTDENT' do A2.indented(A1,A3)
		o 'CALL_START TagBodyList CALL_END' do A2
		o 'Tag' do TagBody.new([A1])
	]

	TagBodyList: [
		o 'TagBodyItem' do TagBody.new([]).add(A1)
		o 'TagBodyList , TagBodyItem' do A1.add A3
		o 'TagBodyList OptComma Terminator TagBodyItem' do A1.add(A3).add(A4)
		o 'TagBodyList OptComma TERMINATOR SEPARATOR Terminator TagBodyItem' do A1.add(A5).add(A6)
		o 'INDENT TagBodyList OptComma Outdent' do A2.indented(A1,A4)
		o 'TagBodyList OptComma INDENT TagBodyList OptComma Outdent' do A1.concat A4
	]

	# Valid arguments are Blocks or Splats.
	TagBodyItem: [
		o 'Expression'
		o '... Expression' do Splat.new(A2).set(keyword: A1)
		o 'Splat'
		o 'LOGIC'
		o 'Comment'
		o 'StyleBlockDeclaration' do A1.set(inTagTree: yes)
	]

	# Class definitions have optional bodies of prototype property assignments,
	# and optional references to the superclass.
	TagDeclaration: [
		o 'TagDeclarationBlock' do A1
		o 'EXTEND TagDeclarationBlock' do A2.set(extension: yes)
		o 'LOCAL TagDeclarationBlock' do A2.set(local: yes)
		# o 'GLOBAL TagDeclarationBlock' do A2.set(global: A1)

	]

	TagDeclarationBlock: [
		o 'TAG TagType' do TagDeclaration.new(A2).set(keyword: A1)
		o 'TAG TagType ClassBody' do TagDeclaration.new(A2, null, A3).set(keyword: A1)
		o 'TAG TagType COMPARE TagType' do TagDeclaration.new(A2, A4).set(keyword: A1, extends: A3)
		o 'TAG TagType COMPARE TagType ClassBody' do TagDeclaration.new(A2, A4, A5).set(keyword: A1, extends: A3)
	]

	# Going to move back to fewer custom tokens
	TagType: [
		o 'TAG_TYPE' do TagTypeIdentifier.new(A1)
	]

	TagId: [
		o '# IDENTIFIER' do TagIdRef.new(A2)
	]

	# Assignment of a variable, property, or index to a value.
	Assign: [
		o 'VarAssign'
		o 'Assignable = Expression' do Assign.new(A2, A1, A3).setEnds(A1,A3)
		o 'Assignable = INDENT Expression Outdent' do Assign.new A2, A1, A4.indented(A3,A5)
	]

	# Assignment when it happens within an object literal. The difference from
	# the ordinary **Assign** is that these allow numbers and strings as keys.
	AssignObj: [
		o '... Expression' do ObjRestAttr.new(A2).set(spread: A1)
		o 'MethodDeclaration' do A1.set(inObject: yes)
		o 'ObjAssignable' do ObjAttr.new(A1)
		o 'ObjAssignable : Expression' do ObjAttr.new(A1, A3)
		o 'ObjAssignable : INDENT Expression Outdent' do ObjAttr.new(A1, A4.indented(A3,A5))
		o 'SimpleObjAssignable = Expression' do ObjAttr.new(A1, null, A3)
		o 'SimpleObjAssignable = INDENT Expression Outdent' do ObjAttr.new(A1, null, A4.indented(A3,A5))
		o 'Comment'
	]

	SimpleObjAssignable: [
		o 'Identifier'
		o 'Identifier Type' do AST.SETTYPE(A1,A2)
		o 'SymbolIdentifier'
		o 'Key'
	]

	ObjAssignable: [
		o 'SimpleObjAssignable'
		# this is the interpolated string
		o '[ Expression ]' do IdentifierExpression.new(A2)
		o '( Expression )' do IdentifierExpression.new(A2)
		o 'AlphaNumeric'
	]

	# A block comment.
	Comment: [
		o 'HERECOMMENT' do Comment.new A1,true
		o 'COMMENT' do Comment.new A1,false
	]

	# The **Code** node is the function literal. It's defined by an indented block
	# of **Block** preceded by a function arrow, with an optional parameter
	# list.
	Code: [
		o 'Method'
		o 'Do'
		o 'Begin'
	]

	Begin: [
		o 'BEGIN Block' do Begin.new A2
	]

	Do: [
		o 'DO Block' do Lambda.new [], A2, null,null, {bound: true, keyword: A1}
		o 'DO BLOCK_PARAM_START ParamList BLOCK_PARAM_END Block' do Lambda.new A3, A5, null, null, {bound: true, keyword: A1}
	]

	# FIXME clean up method
	Method: [
		o 'MethodDeclaration' do A1
		# o 'GLOBAL MethodDeclaration' do A2.set(global: A1)
		o 'STATIC MethodDeclaration' do A2.set(static: A1)
	]

	MethodDeclaration: [
		o 'DEF MethodScope MethodScopeType MethodIdentifier MethodParams MethodBody' do
			MethodDeclaration.new(A5, A6, A4, A2, A3).set(def: A1,keyword: A1, datatype: A4.option('datatype'))

		o 'DEF MethodIdentifier MethodParams MethodBody' do
			MethodDeclaration.new(A3, A4, A2, null).set(def: A1, keyword: A1, datatype: A2.option('datatype') )
	]

	MethodParams: [
		o 'ParamList' do A1
		o 'CALL_START ParamList CALL_END' do A2
	]

	MethodScopeType: [
		o '.' do {static: true}
		o '#' do {}
	]

	MethodIdentifier: [
		o 'Identifier'
		o 'DecoratorIdentifier'
		o 'SymbolIdentifier' do A1.set(as: 'property') # InterpolatedIdentifier.new(A1)
		o '[ Expression ]' do InterpolatedIdentifier.new(A2)
		o 'MethodIdentifier Type' do AST.SETTYPE(A1,A2)
	]

	MethodBody: [
		o 'DEF_BODY Block' do A2
		o 'DEF_BODY DO Block' do A3
		o 'DEF_EMPTY' do Block.new([]).set(end: A1.@loc)
	]

	# should support much more
	MethodScope: [
		o 'MethodIdentifier'
		o 'This'
		o 'Self'
	]

	# An optional, trailing comma.
	OptComma: [
		o ''
		o ','
	]

	OptSemicolon: [
		o ''
		o ';'
	]

	# The list of parameters that a function accepts can be of any length.
	ParamList: [
		o '' do []
		o 'Param' do [A1]
		o 'ParamList , Param' do A1.concat A3
	]

	ParamExpression: [
		o 'Value'
		o 'Code'
		o 'Operation'
		o 'Assign'
		o 'Ternary'
		o 'Tag'
	]

	ParamValue: [
		o 'Expression'
		# o 'Expression'
		# o 'Parenthetical'
		# o 'Operation'
		# o 'Assign'
		# o 'Ternary'
		# o 'Tag'
	]

	# A single parameter in a function definition can be ordinary, or a splat
	# that hoovers up the remaining arguments.
	Param: [
		o 'Object' do Param.new(A1)
		o 'Array' do Param.new(A1)
		o 'ParamVar' # do RequiredParam.new A1
		o '... ParamVar' do A2.set(splat: A1)
		# BlockParam.new A2, null, A1
		o 'BLOCK_ARG ParamVar' do A2.set(blk: A1)
		# BlockParam.new A2, null, A1
		o 'ParamVar = ParamValue' do Param.new(A1.value,A3).set(datatype: A1.option(:datatype))
		o 'Object = ParamValue' do Param.new(A1,A3)
		o 'Array = ParamValue' do Param.new(A1,A3)
		o '...' do RestParam.new(A1)
	]

	# Function Parameters
	ParamVar: [
		o 'Identifier' do Param.new(A1)
		o 'Identifier Type' do AST.SETTYPE(Param.new(A1),A2)
	]

	# A splat that occurs outside of a parameter list.
	Splat: [
		o 'SPLAT Expression' do AST.SPLAT(A2)
	]

	GlobalDeclaration: [
		o 'GLOBAL GlobalAssignable = Expression' do GlobalDeclaration.new(A2,A4).set(keyword: A1,op: A3)
		o 'GLOBAL GlobalAssignable' do GlobalDeclaration.new(A2).set(keyword: A1)
		# o 'GlobalDeclaration = Expression' do A1.set(op: A2, value: A3)
	]

	GlobalAssignable: [
		o 'Identifier'
		o 'Identifier Type' do AST.SETTYPE(A1,A2)
	]


	VarKeyword: [
		o 'VAR'
		o 'LET'
		o 'CONST'
	]

	VarAssignable: [
		o 'Identifier'
		o 'Identifier Type' do AST.SETTYPE(A1,A2)
		o 'Array' # all kinds?
		o 'Object' # not supported anymore
	]

	VarDecl: [
		o 'VarKeyword VarAssignable' do VarReference.new(A2,A1)
		o 'STATIC VarDecl' do A2.set(static: A1)
	]

	VarAssign: [
		o 'VarDecl = Expression' do Assign.new A2, A1, A3
		o 'VarDecl = INDENT Expression Outdent' do Assign.new A2, A1, A4.indented(A3,A5)
	]

	# Variables and properties that can be assigned to.
	SimpleAssignable: [
		o 'ENV_FLAG' do EnvFlag.new(A1)
		o 'Argvar'
		o 'Self' # not sure if self should be assignable really
		o 'Identifier' do VarOrAccess.new(A1) # LocalIdentifier.new(A1)
		o 'SymbolIdentifier' do Access.new('.',null,A1) # LocalIdentifier.new(A1)
		o 'Access'
		# FIXME this isn really assignable?
		# o 'Value . NEW' do LegacyNew.new(A1).set(keyword: A3)
		# o 'Value . Super' do SuperAccess.new('.',A1,A3)
		o 'SimpleAssignable Type' do AST.SETTYPE(A1,A2)
	]

	Access: [
		o 'Value SoakableOp Identifier' do AST.OP(A2,A1,A3) # PropertyAccess.new(A2,A1,A3) # TODO change to regular access
		o 'Value SoakableOp SymbolIdentifier' do IndexAccess.new(A2,A1,A3) # AST.OP(A2,A1,A3) # PropertyAccess.new(A2,A1,A3) # TODO change to regular access
		o 'Value INDEX_START IndexValue INDEX_END' do IndexAccess.new('.',A1,A3.setEnds(A2,A4))
		o 'Value ?. [ IndexValue ]' do AST.OP(A2,A1,A4)
	]

	SoakableOp: [
		'.'
		'?.'
	]

	Super: [
		o 'SUPER' do Super.new(A1)
	]

	# Everything that can be assigned to.
	Assignable: [
		o 'SimpleAssignable'
		o 'Array' #  do A1
		o 'Object' # not supported anymore
	]

	TaggedTemplate: [
		o 'SimpleAssignable InterpolatedString' do TaggedTemplate.new(A1,A2)
		o 'SimpleAssignable String' do TaggedTemplate.new(A1,A2)
	]

	Await: [
		o 'AWAIT Expression' do Await.new(A2).set(keyword: A1)
	]

	# The types of things that can be treated as values -- assigned to, invoked
	# as functions, indexed into, named as a class, etc.
	Value: [
		o 'Assignable'
		o 'Super'
		o 'Literal'
		o 'Literal Type' do AST.SETTYPE(A1,A2)
		o 'Parenthetical'
		o 'Range'
		o 'ARGUMENTS' do AST.ARGUMENTS
		o 'This'
		o 'TagId'
		o 'Selector'
		o 'Invocation'
		o 'TaggedTemplate'
		o 'Require'
		o 'AMPER_REF' do AmperRef.new(A1)
		# TODO Makes no sense that this is not more specific
		o 'Value BANG' do BangCall.new(A1).set(keyword: A2)
	]


	IndexValue: [
		o 'Expression' do Index.new A1
	]

	# In Imba, an object literal is simply a list of assignments.
	Object: [
		o '{ AssignList OptComma }' do Obj.new(A2, A1:generated).setEnds(A1,A4)
	]

	# Assignment of properties within an object literal can be separated by
	# comma, as in JavaScript, or simply by newline.
	AssignList: [
		o '' do AssignList.new([])
		o 'AssignObj' do AssignList.new([A1])
		o 'AssignList , AssignObj' do A1.add A3
		o 'AssignList OptComma Terminator AssignObj' do A1.add(A3).add(A4) # A4.prebreak(A3)
		o 'AssignList OptComma INDENT AssignList OptComma Outdent' do  A1.concat(A4.indented(A3,A6))
	]

	ExpressionList: [
		o 'Expression' do ExpressionList.new([]).add(A1)
		o 'ExpressionList , Expression' do A1.add A3
		o 'ExpressionList OptComma Terminator Expression' do A1.add(A3).add(A4)
		o 'INDENT ExpressionList OptComma Outdent' do A2.indented(A1,A4)
		o 'ExpressionList OptComma INDENT ExpressionList OptComma Outdent' do A1.concat(A4)
	]

	# Class definitions have optional bodies of prototype property assignments,
	# and optional references to the superclass.
	# might as well handle this in the lexer instead

	Class: [
		o 'ClassStart' do A1
		o 'ABSTRACT ClassStart' do A2.set(abstract: A1)
		o 'EXTEND ClassStart' do A2.set(extension: A1)
	]

	ClassStart: [
		# o 'CLASS ClassName ClassConstructorParams ClassBody' do ClassDeclaration.new(A2, null, A4).set(keyword: A1,params: A3)
		# o 'CLASS ClassName ClassConstructorParams' do ClassDeclaration.new(A2, null, []).set(keyword: A1,params: A3)
		o 'CLASS ClassName ClassBody' do ClassDeclaration.new(A2, null, A3).set(keyword: A1)
		o 'CLASS ClassName' do ClassDeclaration.new(A2, null, []).set(keyword: A1)
		o 'CLASS ClassBody' do ClassDeclaration.new(null, null, A2).set(keyword: A1)
		# o 'CLASS ClassName ClassConstructorParams COMPARE Expression ClassBody' do ClassDeclaration.new(A2, A5, A6).set(keyword: A1, params: A3)
		# o 'CLASS ClassName ClassConstructorParams COMPARE Expression' do ClassDeclaration.new(A2, A6, []).set(keyword: A1, params: A3)
		o 'CLASS ClassName COMPARE Expression' do ClassDeclaration.new(A2, A4, []).set(keyword: A1, extends: A3)
		o 'CLASS ClassName COMPARE Expression ClassBody' do ClassDeclaration.new(A2, A4, A5).set(keyword: A1, extends: A3)
		o 'CLASS COMPARE Expression ClassBody' do ClassDeclaration.new(null, A3, A4).set(keyword: A1, extends: A2)
	]

	Class2: [
		o 'CLASS ClassStart' do A2.set(keyword: A1)
		o 'ABSTRACT Class' do A2.set(abstract: A1)
		o 'EXTEND Class' do A2.set(extension: A1)
	]

	ClassStart2: [
		# o 'CLASS ClassName ClassConstructorParams ClassBody' do ClassDeclaration.new(A2, null, A4).set(keyword: A1,params: A3)
		# o 'CLASS ClassName ClassConstructorParams' do ClassDeclaration.new(A2, null, []).set(keyword: A1,params: A3)
		o 'ClassName ClassBody' do ClassDeclaration.new(A1, null, A2)
		o 'ClassName' do ClassDeclaration.new(A1, null, [])
		o 'ClassBody' do ClassDeclaration.new(null, null, A1)
		# o 'CLASS ClassName ClassConstructorParams COMPARE Expression ClassBody' do ClassDeclaration.new(A2, A5, A6).set(keyword: A1, params: A3)
		# o 'CLASS ClassName ClassConstructorParams COMPARE Expression' do ClassDeclaration.new(A2, A6, []).set(keyword: A1, params: A3)
		o 'ClassName COMPARE Expression' do ClassDeclaration.new(A1, A3, []).set(extends: A2)
		o 'ClassName COMPARE Expression ClassBody' do ClassDeclaration.new(A1, A3, A4).set(extends: A2)
		o 'COMPARE Expression ClassBody' do ClassDeclaration.new(null, A2, A3).set(extends: A1)
	]

	ClassName: [
		o 'DecoratorIdentifier'
		o 'Identifier' do VarOrAccess.new(A1) # do ClassName.new(A1)
		o 'SymbolIdentifier' do Access.new('.',null,A1) # IndexAccess.new(A2,A1,A3) # AST.OP(A2,A1,A3)
		o 'ClassName . Identifier' do AST.OP(A2,A1,A3)
		o 'ClassName . SymbolIdentifier' do IndexAccess.new(A2,A1,A3) # AST.OP(A2,A1,A3)
		o 'ClassName Type' do AST.SETTYPE(A1,A2)
	]

	ClassBody: [
		o 'INDENT OUTDENT' do ClassBody.new([]).indented(A1,A2)
		o 'INDENT ClassBodyBlock OUTDENT' do A2.indented(A1,A3)
		o 'INDENT TERMINATOR ClassBodyBlock OUTDENT' do A3.prebreak(A2).indented(A1,A4)
	]

	ClassBodyBlock: [
		o 'ClassBodyLine' do ClassBody.new([]).add(A1)
		o 'ClassBodyBlock Terminator ClassBodyLine' do A1.break(A2).add(A3)
		o 'ClassBodyBlock Terminator' do A1.break(A2)
	]

	ClassBodyLine: [
		o 'Decorators'
		o 'ClassDeclLine'
		o 'Decorators ClassDeclLine' do A1.concat([A2])
		o 'CSSDeclaration'
		o 'RELATION ClassName' do ClassRelation.new(A2).set(keyword: A1)
		o 'Comment'
		o 'Tag'
	]

	ClassDeclLine: [
		o 'STATIC ClassFieldDeclaration' do A2.set(static: A1)
		o 'STATIC MethodDeclaration' do A2.set(static: A1)
		o 'MethodDeclaration'
		o 'ClassFieldDeclaration'
		o 'PROTECTED ClassDeclLine' do A2.set(protected: A1)
		o 'DECLARE ClassDeclLine' do AST.DECLARE(A2,A1)
		o 'ENV_FLAG ClassDeclLine' do A2.addEnv(A1)
	]

	ClassFieldDeclaration: [
		o 'ClassField ClassFieldOp Expression' do A1.set(value: A3,op: A2) # ClassField.new(A1, A3)
		o 'ClassField' do A1
		o 'ClassFieldDeclaration AS AccessorBody' do A1.set(wrapper: A3)
		o 'ClassFieldDeclaration FieldDescriptorFull' do A1.set(wrapper: A2)

	]

	FieldDescriptor: [
		o 'DECORATOR' do Descriptor.new A1 # kinda hacky, should be defined as something else
		o '@ CALL_START Expression CALL_END' do Descriptor.new A3
		# o 'FieldDescriptor ( )' do A1.add(ArgList.new([]))
		# o 'FieldDescriptor ( ArgList )' do A1.add(A3,'!')
		o 'FieldDescriptor Arguments' do A1.add(A2,'!')
		o 'FieldDescriptor INDEX_START ArgList INDEX_END' do A1.add(A3,'=')
		o 'FieldDescriptor . Identifier' do A1.add(A3)
		# o 'FieldDescriptor = Value' do A1.add(A3,'value')
		# o 'FieldDescriptor = DescriptorValue' do A1.add(A3,'value')
	]

	FieldDescriptorFull: [
		o 'FieldDescriptor'
		o 'FieldDescriptor = Expression' do A1.set(default: A3)
		o 'FieldDescriptor Do' do A1.set(callback: A2)
	]

	AccessorBody: [
		'FieldDescriptorFull'
		'Value'
	]

	ClassFieldDecoration: [
		o 'ClassFieldDeclaration @ WatchBody' do A1.set(watch: A3)
	]

	WatchBody: [
		o 'Block'
		o 'Expression'
	]

	ClassFieldOp: [
		'='
		'COMPOUND_ASSIGN'
	]

	ClassField: [
		o 'ClassFieldIdentifier' do ClassField.new(A1)
		o 'PROP ClassFieldIdentifier' do ClassProperty.new(A2).set(keyword: A1)
		o 'ATTR ClassFieldIdentifier' do ClassAttribute.new(A2).set(keyword: A1)
		o 'ClassField Type' do AST.SETTYPE(A1,A2)
		o 'ClassField CALL_START CALL_END' do A1.set(controller: A2)
	]

	ClassFieldIdentifier: [
		o 'Identifier'
		o 'SymbolIdentifier'
	]

	ClassFieldBody: [
		o 'WATCH Expression Terminator' do [A1,A2]
	]

	# Ordinary function invocation, or a chained series of calls.
	Invocation: [
		o 'Value OptFuncExist Arguments' do Call.new(A1, A3, A2)
		o 'Value Do' do A1.addBlock(A2)
	]

	# An optional existence check on a function.
	OptFuncExist: [
		o '' do no
		o 'FUNC_EXIST' do yes
	]

	# The list of arguments to a function call.
	Arguments: [
		o 'CALL_START CALL_END' do ArgList.new([]).setEnds(A1,A2)
		o 'CALL_START ArgList OptComma CALL_END' do A2.setEnds(A1,A4)
	]

	# A reference to the *this* current object.
	This: [
		o 'THIS' do This.new(A1) # Value.new Literal.new 'this'
	]

	Self: [
		o 'SELF' do Self.new(A1)
	]

	# The array literal.
	Array: [
		o '[ ]' do Arr.new(ArgList.new([])).setEnds(A1,A2)
		o '[ ArgList OptComma ]' do Arr.new(A2).setEnds(A1,A4)
		o 'Array Type' do AST.SETTYPE(A1,A2)
	]

	# Inclusive and exclusive range dots.
	# should return the tokens instead
	RangeDots: [
		o '..' do '..'
		o '...' do '...'
	]

	Range: [
		o '[ Expression RangeDots Expression ]' do AST.OP(A3,A2,A4) # Range.new A2, A4, A3
	]

	# The **ArgList** is both the list of objects passed into a function call,
	# as well as the contents of an array literal
	# (i.e. comma-separated expressions). Newlines work as well.
	ArgList: [
		o 'Arg' do ArgList.new([A1])
		o 'ArgList , Arg' do A1.add A3
		o 'ArgList OptComma Terminator Arg' do A1.add(A3).add(A4)
		o 'ArgList OptComma TERMINATOR SEPARATOR Terminator Arg' do A1.add(A5).add(A6)
		o 'INDENT ArgList OptComma Outdent' do A2.indented(A1,A4)
		o 'ArgList OptComma INDENT ArgList OptComma Outdent' do A1.concat A4
	]

	Outdent: [
		o 'Terminator OUTDENT' do A1 # we are going to change how this works
		o 'OUTDENT' do A1
	]

	# Valid arguments are Blocks or Splats.
	Arg: [
		o 'Expression'
		o '... Expression' do Splat.new(A2).set(keyword: A1)
		o 'Splat'
		o 'DO_PLACEHOLDER' do DoPlaceholder.new(A1)
		o 'Comment'
	]

	# Just simple, comma-separated, required arguments (no fancy syntax). We need
	# this to be separate from the **ArgList** for use in **Switch** blocks, where
	# having the newlines wouldn't make sense.
	SimpleArgs: [
		o 'Expression'
		o 'SimpleArgs , Expression' do [].concat A1, A3
	]

	# The variants of *try/catch/finally* exception handling blocks.
	Try: [
		o 'TRY Block' do Try.new A2
		o 'TRY Block Catch' do Try.new A2, A3
		o 'TRY Block Finally' do Try.new A2, null, A3
		o 'TRY Block Catch Finally' do Try.new A2, A3, A4
	]

	Finally: [
		o 'FINALLY Block' do Finally.new A2
	]

	# A catch clause names its error and runs a block of code.
	Catch: [
		o 'CATCH CATCH_VAR Block' do Catch.new(A3,A2)
		o 'CATCH Block' do Catch.new(A2,null)
		# o 'CATCH CATCH_VAR Expression' do Catch.new(A3,A2)
	]

	# Throw an exception object.
	Throw: [
		o 'THROW Expression' do Throw.new A2
	]

	# Parenthetical expressions. Note that the **Parenthetical** is a **Value**,
	# not an **Expression**, so if you need to use an expression in a place
	# where only values are accepted, wrapping it in parentheses will always do
	# the trick.
	Parenthetical: [
		o '( ExpressionList )' do Parens.new(A2,A1,A3)
		o '( ExpressionList ) UNIT' do ExpressionWithUnit.new(Parens.new(A2,A1,A3),A4)
		o 'Parenthetical Type' do AST.SETTYPE(A1,A2)
		# o '( INDENT ExpressionList OUTDENT )' do Parens.new(A3,A1,A5)
	]

	# The condition portion of a while loop.
	WhileSource: [
		o 'WHILE Expression' do While.new(A2, keyword: A1)
		o 'WHILE Expression WHEN Expression' do While.new(A2, guard: A4, keyword: A1)
		o 'UNTIL Expression' do While.new(A2, invert: true, keyword: A1)
		o 'UNTIL Expression WHEN Expression' do While.new(A2, invert: true, guard: A4, keyword: A1)
	]

	# The while loop can either be normal, with a block of expressions to execute,
	# or postfix, with a single expression. There is no do..while.
	# should be solved by POST_WHILE instead
	While: [
		o 'WhileSource Block' do A1.addBody A2
		o 'Statement  WhileSource' do A2.addBody Block.wrap [A1]
		o 'Expression WhileSource' do A2.addBody Block.wrap [A1]
		o 'Loop' do A1
	]

	# should deprecate
	Loop: [
		o 'LOOP Block' do While.new(Literal.new 'true', keyword: A1).addBody A2
		o 'LOOP Expression' do While.new(Literal.new 'true', keyword: A1).addBody Block.wrap [A2]
	]

	# Array, object, and range comprehensions, at the most generic level.
	# Comprehensions can either be normal, with a block of expressions to execute,
	# or postfix, with a single expression.
	For: [
		o 'Statement  ForBody' do A2.addBody([A1])
		o 'Expression ForBody' do A2.addBody([A1])
		o 'ForBody    Block' do A1.addBody(A2)
		o 'ForBody    Block ELSE Block' do A1.addBody(A2).addElse(A4)
	]

	ForKeyword: [
		o 'FOR'
		o 'POST_FOR'
	]

	ForBody: [
		o 'ForKeyword Range' do source: ValueNode.new(A2)
		o 'ForStart ForSource' do A2.configure(own: A1:own, await: (A1:await), name: A1[0], index: A1[1], keyword: A1:keyword, params: A1)
	]

	ForStart: [
		o 'ForKeyword ForVariables' do (A2:keyword = A1) && A2
		# should link to the actual keyword instead
		o 'ForKeyword AWAIT ForVariables' do (A3:await = A2) && (A3:keyword = A1) && A3
		o 'ForKeyword OWN ForVariables' do (A3:own = yes) && (A3:keyword = A1) && A3

	]

	# An array of all accepted values for a variable inside the loop.
	# This enables support for pattern matching.
	ForValue: [
		o 'Identifier'
		o 'Identifier Type' do AST.SETTYPE(A1,A2)
		o 'Array' # do ValueNode.new A1
		o 'Object' # do ValueNode.new A1
	]

	# An array or range comprehension has variables for the current element
	# and (optional) reference to the current index. Or, *key, value*, in the case
	# of object comprehensions.
	ForVariables: [
		o 'ForValue' do [A1]
		o 'ForValue , ForValue' do [A1, A3]
		o 'ForValue , ForValue , ForValue' do [A1, A3, A5]
	]

	# The source of a comprehension is an array or object with an optional guard
	# clause. If it's an array comprehension, you can also choose to step through
	# in fixed-size increments.
	ForSource: [
		o 'FORIN Expression' do ForIn.new source: A2
		o 'FOROF Expression' do ForOf.new source: A2, object: yes
		o 'FORIN Expression WHEN Expression' do ForIn.new source: A2, guard: A4
		o 'FOROF Expression WHEN Expression' do ForOf.new source: A2, guard: A4, object: yes
		o 'FORIN Expression BY Expression' do ForIn.new source: A2, step:  A4
		o 'FORIN Expression WHEN Expression BY Expression' do ForIn.new source: A2, guard: A4, step: A6
		o 'FORIN Expression BY Expression WHEN Expression' do ForIn.new source: A2, step:  A4, guard: A6
	]

	Switch: [
		o 'SWITCH Expression INDENT Whens OUTDENT' do Switch.new A2, A4
		o 'SWITCH Expression INDENT Whens ELSE Block Outdent' do Switch.new A2, A4, A6
		o 'SWITCH INDENT Whens OUTDENT' do Switch.new null, A3
		o 'SWITCH INDENT Whens ELSE Block OUTDENT' do Switch.new null, A3, A5
	]

	Whens: [
		o 'When'
		o 'Whens When' do A1.concat A2
	]

	# An individual **When** clause, with action.
	When: [
		o 'LEADING_WHEN SimpleArgs Block' do [SwitchCase.new(A2, A3)]
		o 'LEADING_WHEN SimpleArgs Block TERMINATOR' do [SwitchCase.new(A2, A3)]
	]

	# The most basic form of *if* is a condition and an action. The following
	# if-related rules are broken up along these lines in order to avoid
	# ambiguity.

	IfBlock: [
		o 'IF Expression Block' do If.new(A2, A3, type: A1)
		o 'IfBlock ELSE IF Expression Block' do A1.addElse If.new(A4, A5, type: A3)

		# seems like this refers to the wrong blocks no?
		o 'IfBlock ELIF Expression Block' do
			A1.addElse If.new(A3, A4, type: A2)

		o 'IfBlock ELSE Block' do A1.addElse(A3.set(keyword: A2))
	]

	# The full complement of *if* expressions, including postfix one-liner
	# *if* and *unless*.
	If: [
		o 'IfBlock'
		o 'Statement  POST_IF Expression' do If.new A3, Block.new([A1]), type: A2, statement: true
		o 'Expression POST_IF Expression' do If.new A3, Block.new([A1]), type: A2 # , statement: true # why is this a statement?!?
	]

	Ternary: [
		o 'Expression ? Expression : Expression' do AST.If.ternary(A1,A3,A5)
	]

	# Arithmetic and logical operators, working on one or more operands.
	# Here they are grouped by order of precedence. The actual precedence rules
	# are defined at the bottom of the page. It would be shorter if we could
	# combine most of these rules into a single generic *Operand OpSymbol Operand*
	# -type rule, but in order to make the precedence binding possible, separate
	# rules are necessary.
	Operation: [
		o 'NEW Expression' do AST.Instantiation.for(A2,A1).setEnds(A1,A2)
		o 'UNARY Expression' do AST.OP(A1, A2).setEnds(A1,A2)
		o '--- Expression' do AST.OP(A1,A2).setEnds(A1,A2)
		o '+++ Expression' do AST.OP(A1,A2).setEnds(A1,A2)
		o('- Expression', &, prec: 'UNARY') do AST.OP(A1,A2).setEnds(A1,A2)
		o('+ Expression', &, prec: 'UNARY') do AST.OP(A1,A2).setEnds(A1,A2)
		o '-- SimpleAssignable' do UnaryOp.new(A1, null, A2).setEnds(A1,A2)
		o '++ SimpleAssignable' do UnaryOp.new(A1, null, A2).setEnds(A1,A2)
		o 'SimpleAssignable --' do UnaryOp.new(A2, A1, null, true).setEnds(A1,A2)
		o 'SimpleAssignable ++' do UnaryOp.new(A2, A1, null, true).setEnds(A1,A2)

		o 'Expression + Expression' do Op.new(A2,A1,A3).setEnds(A1,A3)
		o 'Expression - Expression' do Op.new(A2,A1,A3).setEnds(A1,A3)

		o 'Expression EXP      Expression' do AST.OP(A2, A1, A3).setEnds(A1,A3)
		o 'Expression MATH     Expression' do AST.OP(A2, A1, A3).setEnds(A1,A3)
		o 'Expression SHIFT    Expression' do AST.OP(A2, A1, A3).setEnds(A1,A3)
		o 'Expression COMPARE  Expression' do AST.OP(A2, A1, A3).setEnds(A1,A3)
		o 'Expression LOGIC    Expression' do AST.OP(A2, A1, A3).setEnds(A1,A3)

		o 'Expression NOT RELATION Expression' do
			AST.OP(A3, A1, A4).invert(A2)

		o 'Expression RELATION Expression' do
			AST.OP(A2, A1, A3).setEnds(A1,A3)

		o 'SimpleAssignable COMPOUND_ASSIGN Expression' do AST.OP(A2,A1,A3)
		o 'SimpleAssignable COMPOUND_ASSIGN INDENT Expression Outdent' do AST.OP(A2,A1,A4.indented(A3,A5))
	]

# Precedence
# ----------

var operators = [
	['left',      '.', '?.', '::','.:']
	['left',      'CALL_START', 'CALL_END']
	# ['left',      '{{', '}}']
	# ['left', 'STRING_START','STRING_END']
	['nonassoc',  '++', '--']
	['right',     'UNARY','NEW','THROW','NOT']
	['right','AWAIT']
	['right',     'EXP']
	['left',      'MATH']
	['left',      '+', '-','+++','---']
	['left',      'SHIFT']
	['left',      'RELATION']
	['left',      'COMPARE']
	['left',      'LOGIC']
	['right',      '?']
	['nonassoc',  'INDENT', 'OUTDENT']
	['right',     '=', ':', 'COMPOUND_ASSIGN', 'RETURN','YIELD', 'THROW', 'EXTENDS']
	['right',     'FORIN', 'FOROF', 'BY', 'WHEN']
	['right',     'TAG_END']
	['right',     'IF', 'ELSE', 'FOR', 'DO', 'WHILE', 'UNTIL', 'LOOP', 'SUPER','CLASS', 'MIXIN','INTERFACE', 'MODULE', 'TAG', 'EVENT', 'TRIGGER', 'TAG_END', 'IMPORT', 'EXPORT']
	['right',     'POST_IF','POST_FOR']
	['right', 'NEW_TAG']
	['right', 'TAG_ATTR_SET']
	['right', 'SPLAT']
	['left', 'SELECTOR_START']
	['left', 'CSS']
]

# Wrapping Up
# -----------

# Finally, now that we have our **grammar** and our **operators**, we can create
# our **Jison.Parser**. We do this by processing all of our rules, recording all
# terminals (every symbol which does not appear as the name of a rule above)
# as "tokens".

var tokens = []
for name, alternatives of grammar
	grammar[name] = for alt in alternatives
		for token in alt[0].split(' ')
			tokens.push token unless grammar[token]
		alt[1] = "return {alt[1]}" if name is 'Root'
		alt

# Initialize the **Parser** with our list of terminal **tokens**, our **grammar**
# rules, and the name of the root. Reverse the operators because Jison orders
# precedence from low to high, and we have it high to low
# (as in [Yacc](http://dinosaur.compilertools.net/yacc/index.html)).

exports:parser = Parser.new
	tokens: tokens.join(' ')
	bnf: grammar
	operators: operators.reverse
	startSymbol: 'Root'
