public class Equality extends BinaryOperation {
	@Nonnull
	public final Operator operator;
	@Nonnull
	public final NodeWithValue left;
	@Nonnull
	public final NodeWithValue right;


	public Equality(@Nonnull Operator operator, @Nonnull NodeWithValue left, @Nonnull NodeWithValue right) {
		this.operator = operator;
		this.left = left;
		this.right = right;
	}

	@Override
	@Nonnull
	public NodeWithValue left() {
		return this.left;
	}

	@Override
	@Nonnull
	public NodeWithValue right() {
		return this.right;
	}

	public enum Operator implements BinaryOperator { // TODO could get by with as few as one of these
		Eq("=="),
		Neq("!="),
		StrictEq("==="),
		StrictNeq("!==");

		@Nonnull
		public final String name;

		Operator(@Nonnull String name) {
			this.name = name;
		}
	}
} public class FloatMath extends BinaryOperation {
	@Nonnull
	public final Operator operator;
	@Nonnull
	public final NodeWithValue left;
	@Nonnull
	public final NodeWithValue right;


	public FloatMath(@Nonnull Operator operator, @Nonnull NodeWithValue left, @Nonnull NodeWithValue right) {
		this.operator = operator;
		this.left = left;
		this.right = right;
	}

	@Override
	@Nonnull
	public NodeWithValue left() {
		return this.left;
	}

	@Override
	@Nonnull
	public NodeWithValue right() {
		return this.right;
	}

	public enum Operator implements BinaryOperator {
		Plus("+"),
		Minus("-"),
		Mul("*"),
		Div("/"),
		Rem("%"),
		Exp("**");

		@Nonnull
		public final String name;

		Operator(@Nonnull String name) {
			this.name = name;
		}
	}
} public class In extends BinaryOperation {
	@Nonnull
	public final NodeWithValue left;


	@Nonnull

	public final NodeWithValue right;

	@Override
	@Nonnull
	public NodeWithValue left() {
		return this.left;
	}

	@Override
	@Nonnull
	public NodeWithValue right() {
		return this.right;
	}

	public In(@Nonnull NodeWithValue left, @Nonnull NodeWithValue right) {
		this.left = left;
		this.right = right;
	}
} public class InstanceOf extends BinaryOperation {
	@Nonnull
	public final NodeWithValue left;


	@Nonnull
	public final NodeWithValue right;

	@Override
	@Nonnull
	public NodeWithValue left() {
		return this.left;
	}

	@Override
	@Nonnull
	public NodeWithValue right() {
		return this.right;
	}

	public InstanceOf(@Nonnull NodeWithValue left, @Nonnull NodeWithValue right) {
		this.left = left;
		this.right = right;
	}
} public class IntMath extends BinaryOperation {
	@Nonnull
	public final Operator operator;
	@Nonnull
	public final NodeWithValue left;
	@Nonnull
	public final NodeWithValue right;


	public IntMath(@Nonnull Operator operator, @Nonnull NodeWithValue left, @Nonnull NodeWithValue right) {
		this.operator = operator;
		this.left = left;
		this.right = right;
	}

	@Override
	@Nonnull
	public NodeWithValue left() {
		return this.left;
	}

	@Override
	@Nonnull
	public NodeWithValue right() {
		return this.right;
	}

	public enum Operator implements BinaryOperator {
		BitwiseAnd("&"),
		BitwiseOr("|"),
		BitwiseXor("^"),
		LeftShift("<<"),
		RightShift(">>"),
		UnsignedRightShift(">>>");

		@Nonnull
		public final String name;

		Operator(@Nonnull String name) {
			this.name = name;
		}
	}
} public class Logic extends BinaryOperation {
	@Nonnull
	public final Operator operator;
	@Nonnull
	public final NodeWithValue left;


	@Nonnull

	public final NodeWithValue right;

	public Logic(@Nonnull Operator operator, @Nonnull NodeWithValue left, @Nonnull NodeWithValue right) {
		this.operator = operator;
		this.left = left;
		this.right = right;
	}

	@Override
	@Nonnull
	public NodeWithValue left() {
		return this.left;
	}

	@Override
	@Nonnull
	public NodeWithValue right() {
		return this.right;
	}

	public enum Operator implements BinaryOperator {
		And("&&"),
		Or("||");

		@Nonnull
		public final String name;

		Operator(@Nonnull String name) {
			this.name = name;
		}
	}

} public class RelationalComparison extends BinaryOperation {
	@Nonnull
	public final Operator operator;
	@Nonnull
	public final NodeWithValue left;


	@Nonnull

	public final NodeWithValue right;

	public RelationalComparison(@Nonnull Operator operator, @Nonnull NodeWithValue left, @Nonnull NodeWithValue right) {
		this.operator = operator;
		this.left = left;
		this.right = right;
	}

	@Override
	@Nonnull
	public NodeWithValue left() {
		return this.left;
	}

	@Override
	@Nonnull
	public NodeWithValue right() {
		return this.right;
	}

	public enum Operator implements BinaryOperator {
		LessThan("<"),
		LessThanEqual("<="),
		GreaterThan(">"),
		GreaterThanEqual(">=");

		@Nonnull
		public final String name;

		Operator(@Nonnull String name) {
			this.name = name;
		}
	}
} public class Block implements Node {
	@Nonnull
	public final ImmutableList<Node> children;

	// Note: Directives are being added to blocks in the cases where we are transforming the AST with new blocks and want
	// to ensure the directives are being applied to the block (i.e ensuring directives in IIFE's are executed). This is
	// not meant for users inserting directives in their code for blocks, but for the explicator to populate in situations
	// needing this functionality.
	@Nonnull
	public final ImmutableList<Directive> directives;

	public Block(@Nonnull ImmutableList<Node> children) {
		this.children = children;
		this.directives = ImmutableList.empty();
	}

	public Block(@Nonnull ImmutableList<Node> children, @Nonnull ImmutableList<Directive> directives) {
		this.children = children;
		this.directives = directives;
	}

	public Block(@Nonnull Node child) {
		this.children = ImmutableList.of(child);
		this.directives = ImmutableList.empty();
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof Block && this.directives.equals(((Block) node).directives);
	}

	public boolean isNoOp() {
		return !children.exists(c -> { // is no-op <==> no non-trivial ops
			if (c instanceof Void || c instanceof Literal || c instanceof LocalReference) { // TODO it might probably be better for each node to have a isNoOp method, which this method could call. or have a no-op interface, maybe.
				return false;
			} else if (c instanceof Block) {
				return !((Block) c).isNoOp();
			} else {
				return true;
			}
		});
	}
} public class BlockWithValue implements NodeWithValue {
	@Nonnull
	public Block head;
	@Nonnull
	public NodeWithValue result;


	public BlockWithValue(@Nonnull Block head, @Nonnull NodeWithValue result) {
		this.head = head;
		this.result = result;
	}

	public BlockWithValue(@Nonnull ImmutableList<Node> children, @Nonnull NodeWithValue result) {
		this.head = new Block(children);
		this.result = result;
	}
} public class Break implements Node {
	@Nonnull
	public final BreakTarget target;

	@Nonnull
	public final ImmutableList<BrokenThrough> broken;


	public Break(@Nonnull BreakTarget target, @Nonnull ImmutableList<BrokenThrough> broken) {
		this.target = target;
		this.broken = broken;
	}


	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof Break && this.broken.equals(((Break) node).broken);
	}
} public class BreakTarget implements Node { // no-op. target of a break.

	public BreakTarget() {
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node == this;
	}
} public class Call implements NodeWithValue {
	@Nonnull
	public final NodeWithValue callee;


	@Nonnull
	public final ImmutableList<NodeWithValue> arguments;

	public Call(@Nonnull NodeWithValue callee, @Nonnull ImmutableList<NodeWithValue> arguments) {
		this.callee = callee;
		this.arguments = arguments;
	}
} public class DeleteGlobalProperty implements NodeWithValue {


	@Nonnull
	public final String which;

	public DeleteGlobalProperty(@Nonnull String which) {
		this.which = which;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof DeleteGlobalProperty && this.which.equals(((DeleteGlobalProperty) node).which);
	}
} public class DeleteProperty implements NodeWithValue {


	@Nonnull
	public final NodeWithValue object;
	@Nonnull
	public final NodeWithValue fieldExpression;
	public final boolean strict;

	public DeleteProperty(@Nonnull NodeWithValue object, @Nonnull NodeWithValue fieldExpression, boolean strict) {
		this.object = object;
		this.fieldExpression = fieldExpression;
		this.strict = strict;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof DeleteProperty && this.strict == ((DeleteProperty) node).strict;
	}
} public class GlobalReference implements NodeWithValue {


	@Nonnull
	public final String name;

	public GlobalReference(@Nonnull String name) {
		this.name = name;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof GlobalReference && this.name.equals(((GlobalReference) node).name);
	}
} public class Halt implements NodeWithValue {
	@Nonnull
	public static final Halt INSTANCE = new Halt();


	private Halt() {
	}

} public class IfElse implements Node {
	@Nonnull
	public final NodeWithValue test; // TODO could be a type coercion node


	@Nonnull
	public final Block consequent;
	@Nonnull
	public final Block alternate;

	public IfElse(@Nonnull NodeWithValue test, @Nonnull Block consequent, @Nonnull Block alternate) {
		this.test = test;
		this.consequent = consequent;
		this.alternate = alternate;
	}
} public class Keys implements NodeWithValue {


	@Nonnull
	public final NodeWithValue _object;

	public Keys(@Nonnull NodeWithValue object) {
		_object = object;
	}
} public class LiteralBoolean implements Literal {


	public final boolean value;

	public LiteralBoolean(boolean value) {
		this.value = value;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof LiteralBoolean && this.value == ((LiteralBoolean) node).value;
	}
} public class LiteralEmptyArray implements Literal {
	@Nonnull
	public static final LiteralEmptyArray INSTANCE = new LiteralEmptyArray();


	private LiteralEmptyArray() {
	}
} public class LiteralEmptyObject implements Literal {
	@Nonnull
	public static final LiteralEmptyObject INSTANCE = new LiteralEmptyObject();


	private LiteralEmptyObject() {
	}
} public class LiteralFunction implements Literal {
	@Nonnull
	public final Maybe<Variable> name;
	@Nonnull
	public final Maybe<Variable> arguments;
	@Nonnull
	public final ImmutableList<Variable> parameters;
	@Nonnull
	public final ImmutableList<Variable> locals;


	@Nonnull
	public final ImmutableList<Variable> captured;
	@Nonnull
	public final Block body;
	@Nonnull
	public final boolean isStrict;

	public LiteralFunction(
		@Nonnull Maybe<Variable> name, @Nonnull Maybe<Variable> arguments, @Nonnull ImmutableList<Variable> parameters, @Nonnull ImmutableList<Variable> locals,
		@Nonnull ImmutableList<Variable> captured, @Nonnull Block body, boolean isStrict
	) {
		this.name = name;
		this.arguments = arguments;
		this.parameters = parameters;
		this.locals = locals;
		this.captured = captured;
		this.body = body;
		this.isStrict = isStrict;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof LiteralFunction &&
			this.name.equals(((LiteralFunction) node).name) &&
			this.arguments.equals(((LiteralFunction) node).arguments) &&
			this.parameters.equals(((LiteralFunction) node).parameters) &&
			this.locals.equals(((LiteralFunction) node).locals) &&
			this.captured.equals(((LiteralFunction) node).captured) &&
			this.isStrict == ((LiteralFunction) node).isStrict;
	}
} public class LiteralInfinity implements Literal {
	@Nonnull
	public static final LiteralInfinity INSTANCE = new LiteralInfinity();


	private LiteralInfinity() {
	}
} public class LiteralNull implements Literal {
	@Nonnull
	public static final LiteralNull INSTANCE = new LiteralNull();


	private LiteralNull() {
	}

} public class LiteralNumber implements Node, Literal {


	public final double value;

	public LiteralNumber(double value) {
		this.value = value;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof LiteralNumber && this.value == ((LiteralNumber) node).value;
	}
} public class LiteralRegExp implements Literal {
	@Nonnull
	public final String pattern;
	public final boolean global;
	public final boolean ignoreCase;
	public final boolean multiLine;
	public final boolean sticky;
	public final boolean unicode;


	public LiteralRegExp(@Nonnull String pattern, boolean global, boolean ignoreCase, boolean multiLine, boolean sticky, boolean unicode) {
		this.pattern = pattern;
		this.global = global;
		this.ignoreCase = ignoreCase;
		this.multiLine = multiLine;
		this.sticky = sticky;
		this.unicode = unicode;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof LiteralRegExp &&
			this.pattern.equals(((LiteralRegExp) node).pattern) &&
			this.global == ((LiteralRegExp) node).global &&
			this.ignoreCase == ((LiteralRegExp) node).ignoreCase &&
			this.multiLine == ((LiteralRegExp) node).ignoreCase &&
			this.sticky == ((LiteralRegExp) node).sticky &&
			this.unicode == ((LiteralRegExp) node).unicode;
	}
} public class LiteralString implements Literal {
	@Nonnull
	public final String value;


	public LiteralString(@Nonnull String value) {
		this.value = value;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof LiteralString && this.value.equals(((LiteralString) node).value);
	}
} public class LiteralSymbol implements Literal {
	@Nonnull
	public final String description;


	public LiteralSymbol(@Nonnull String description) {
		this.description = description;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof LiteralSymbol && this.description.equals(((LiteralSymbol) node).description);
	}
} public class LiteralUndefined implements Literal {
	@Nonnull
	public static final LiteralUndefined INSTANCE = new LiteralUndefined();


	private LiteralUndefined() {
	}
} public class LocalReference implements NodeWithValue {
	@Nonnull
	public Variable variable;


	public LocalReference(@Nonnull Variable variable) {
		this.variable = variable;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node.getClass() == LocalReference.class && this.variable.equals(((LocalReference) node).variable);
	}
} public class Loop implements Node {
	@Nonnull
	public final Block block;


	public Loop(@Nonnull Block block) {
		this.block = block;
	}
} public class MemberAccess implements NodeWithValue {
	@Nonnull
	public final NodeWithValue object;


	@Nonnull
	public final NodeWithValue fieldExpression;

	public MemberAccess(@Nonnull NodeWithValue object, @Nonnull NodeWithValue fieldExpression) {
		this.object = object;
		this.fieldExpression = fieldExpression;
	}
} public class MemberAssignment implements NodeWithValue {
	@Nonnull
	public final NodeWithValue object;
	@Nonnull
	public final NodeWithValue fieldExpression;
	@Nonnull
	public final MemberAssignmentProperty.StaticValue property;
	public final boolean strict;


	public MemberAssignment(
		@Nonnull NodeWithValue object, @Nonnull NodeWithValue fieldExpression,
		@Nonnull MemberAssignmentProperty.StaticValue property,
		boolean strict
	) {
		this.object = object;
		this.fieldExpression = fieldExpression;
		this.property = property;
		this.strict = strict;
	}

	public MemberAssignment(
		@Nonnull NodeWithValue object, @Nonnull NodeWithValue fieldExpression, @Nonnull NodeWithValue staticValue, boolean strict
	) {
		this.object = object;
		this.fieldExpression = fieldExpression;
		this.property = new MemberAssignmentProperty.StaticValue(staticValue);
		this.strict = strict;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof MemberAssignment && this.strict == ((MemberAssignment) node).strict;
	}
} public interface MemberAssignmentProperty extends Node {
	class StaticValue implements MemberAssignmentProperty {
		@Nonnull
		public final NodeWithValue value;

		public StaticValue(@Nonnull NodeWithValue value) {
			this.value = value;
		}
	}

	class Getter implements MemberAssignmentProperty {
		@Nonnull
		public final LiteralFunction value;

		public Getter(@Nonnull LiteralFunction value) {
			this.value = value;
		}
	}

	class Setter implements MemberAssignmentProperty {
		@Nonnull
		public final LiteralFunction value;

		public Setter(@Nonnull LiteralFunction value) {
			this.value = value;
		}
	}
} public class MemberCall implements NodeWithValue {
	@Nonnull
	public final NodeWithValue object;

	@Nonnull
	public final NodeWithValue fieldExpression;

	@Nonnull
	public final ImmutableList<NodeWithValue> arguments;

	public MemberCall(@Nonnull NodeWithValue object, @Nonnull NodeWithValue fieldExpression, @Nonnull ImmutableList<NodeWithValue> arguments) {
		this.object = object;
		this.fieldExpression = fieldExpression;
		this.arguments = arguments;
	}


} public class MemberDefinition implements Node {
	@Nonnull
	public final NodeWithValue object;
	@Nonnull
	public final NodeWithValue fieldExpression;


	@Nonnull
	public final MemberAssignmentProperty property;

	public MemberDefinition(
		@Nonnull NodeWithValue object, @Nonnull NodeWithValue fieldExpression, @Nonnull MemberAssignmentProperty property
	) {
		this.object = object;
		this.fieldExpression = fieldExpression;
		this.property = property;
	}

	public MemberDefinition(
		@Nonnull NodeWithValue object, @Nonnull NodeWithValue fieldExpression, @Nonnull NodeWithValue staticValue
	) {
		this.object = object;
		this.fieldExpression = fieldExpression;
		this.property = new MemberAssignmentProperty.StaticValue(staticValue);
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof MemberDefinition && this.property.getClass() == ((MemberDefinition) node).property.getClass();
	}
} public class New implements NodeWithValue {
	@Nonnull
	public final NodeWithValue callee;
	@Nonnull
	public final ImmutableList<NodeWithValue> arguments;


	public New(@Nonnull NodeWithValue callee, @Nonnull ImmutableList<NodeWithValue> arguments) {
		this.callee = callee;
		this.arguments = arguments;
	}
} public interface Node {

	default boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node.getClass() == this.getClass();
	}

} public class RequireObjectCoercible implements NodeWithValue {
	@Nonnull
	public final NodeWithValue expression;


	public RequireObjectCoercible(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class Return implements Node {
	@Nonnull
	public final Maybe<NodeWithValue> expression;


	public Return(@Nonnull Maybe<NodeWithValue> expression) {
		this.expression = expression;
	}

	public Return(@Nonnull NodeWithValue expression) {
		this.expression = Maybe.of(expression);
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof Return && this.expression.isJust() == ((Return) node).expression.isJust();
	}
} public class ReturnAfterFinallies implements Node {
	@Nonnull
	public final Maybe<LocalReference> savedValue;

	@Nonnull
	public final ImmutableList<BrokenThrough> broken;

	public ReturnAfterFinallies(@Nonnull Maybe<LocalReference> savedValue, @Nonnull ImmutableList<BrokenThrough> broken) {
		this.savedValue = savedValue;
		this.broken = broken;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof ReturnAfterFinallies &&
			this.savedValue.isJust() == ((ReturnAfterFinallies) node).savedValue.isJust()
			&& this.broken.equals(((ReturnAfterFinallies) node).broken);
	}

} public class SwitchStatement implements Node {
	@Nonnull
	public final LocalReference discriminant;
	@Nonnull
	public final ImmutableList<Pair<NodeWithValue, Block>> preDefaultCases;


	@Nonnull

	public final Block defaultCase;
	@Nonnull
	public final ImmutableList<Pair<NodeWithValue, Block>> postDefaultCases;

	public SwitchStatement(
		@Nonnull LocalReference discriminant, @Nonnull ImmutableList<Pair<NodeWithValue, Block>> preDefaultCases,
		@Nonnull Block defaultCase, @Nonnull ImmutableList<Pair<NodeWithValue, Block>> postDefaultCases
	) {
		this.discriminant = discriminant;
		this.preDefaultCases = preDefaultCases;
		this.defaultCase = defaultCase;
		this.postDefaultCases = postDefaultCases;
	}
} public class TemporaryReference extends LocalReference {
	public TemporaryReference() {
		super(new Variable(
			"LOCAL",
			ImmutableList.empty(),
			ImmutableList.empty()
		)); // TODO consider if this is what we actually want to do
	}

	// This is used in ReconstructingReducer
	public TemporaryReference(@Nonnull Variable variable) {
		super(variable);
	}
} public class This implements NodeWithValue {
	public final boolean strict;


	public This(boolean strict) {
		this.strict = strict;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof This && this.strict == ((This) node).strict;
	}
} public class Throw implements Node {
	@Nonnull
	public final NodeWithValue expression;


	public Throw(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class TryCatch implements Node {
	@Nonnull
	public final Block tryBody;
	@Nonnull
	public final Pair<Variable, Block> catchBody;


	public TryCatch(@Nonnull Block tryBody, @Nonnull Pair<Variable, Block> catchBody) {
		this.tryBody = tryBody;
		this.catchBody = catchBody;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof TryCatch && this.catchBody.left.equals(((TryCatch) node).catchBody.left);
	}
} public class TryFinally implements Node {
	@Nonnull
	public final Block tryBody;

	@Nonnull
	public final Block finallyBody;


	public TryFinally(@Nonnull Block tryBody, @Nonnull Block finallyBody) {
		this.tryBody = tryBody;
		this.finallyBody = finallyBody;
	}
} public class TypeCoercionNumber implements NodeWithValue {
	@Nonnull
	public final NodeWithValue expression;


	public TypeCoercionNumber(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class TypeCoercionObject implements NodeWithValue {
	@Nonnull
	public final NodeWithValue expression;


	public TypeCoercionObject(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class TypeCoercionString implements NodeWithValue {
	@Nonnull
	public final NodeWithValue expression;


	public TypeCoercionString(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class TypeofGlobal implements NodeWithValue {
	@Nonnull
	public final String which;


	public TypeofGlobal(@Nonnull String which) {
		this.which = which;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof TypeofGlobal && this.which.equals(((TypeofGlobal) node).which);
	}
} public class BitwiseNot extends UnaryOperation {
	@Nonnull
	public final NodeWithValue expression;

	@Override
	@Nonnull
	public NodeWithValue expression() {
		return expression;
	}


	public BitwiseNot(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class Negation extends UnaryOperation {


	@Nonnull
	public final NodeWithValue expression;

	@Nonnull
	public NodeWithValue expression() {
		return this.expression;
	}

	public Negation(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class Not extends UnaryOperation {


	@Nonnull
	public final NodeWithValue expression;

	@Nonnull
	public NodeWithValue expression() {
		return this.expression;
	}

	public Not(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class Typeof extends UnaryOperation {
	@Nonnull
	public final NodeWithValue expression;

	@Nonnull
	public NodeWithValue expression() {
		return this.expression;
	}


	public Typeof(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class VoidOp extends UnaryOperation {


	@Nonnull
	public final NodeWithValue expression;

	@Nonnull
	public NodeWithValue expression() {
		return this.expression;
	}

	public VoidOp(@Nonnull NodeWithValue expression) {
		this.expression = expression;
	}
} public class VariableAssignment implements NodeWithValue {


	@Nonnull
	public final Either<GlobalReference, LocalReference> ref;
	@Nonnull
	public final NodeWithValue value;
	public final boolean strict;

	public VariableAssignment(
		@Nonnull Either<GlobalReference, LocalReference> ref, @Nonnull NodeWithValue value, boolean strict
	) {
		this.ref = ref;
		this.value = value;
		this.strict = strict;
	}

	public VariableAssignment(@Nonnull GlobalReference ref, @Nonnull NodeWithValue value, boolean strict) {
		this.ref = Either.left(ref);
		this.value = value;
		this.strict = strict;
	}

	public VariableAssignment(@Nonnull LocalReference ref, @Nonnull NodeWithValue value, boolean strict) {
		this.ref = Either.right(ref);
		this.value = value;
		this.strict = strict;
	}

	@Override
	public boolean equalsIgnoringChildren(@Nonnull Node node) {
		return node instanceof VariableAssignment && this.strict == ((VariableAssignment) node).strict;
	}
} public class Void implements Node {
	@Nonnull
	public static final Void INSTANCE = new Void();


	private Void() {
	}
}
import com.shapesecurity.functional.Pair;
import com.shapesecurity.functional.data.ImmutableList;
import com.shapesecurity.functional.data.Maybe;
import com.shapesecurity.functional.data.Monoid;
import com.shapesecurity.shift.es2017.semantics.asg.BinaryOperation.Equality;
import com.shapesecurity.shift.es2017.semantics.asg.BinaryOperation.FloatMath;
import com.shapesecurity.shift.es2017.semantics.asg.BinaryOperation.In;
import com.shapesecurity.shift.es2017.semantics.asg.BinaryOperation.InstanceOf;
import com.shapesecurity.shift.es2017.semantics.asg.BinaryOperation.IntMath;
import com.shapesecurity.shift.es2017.semantics.asg.BinaryOperation.Logic;
import com.shapesecurity.shift.es2017.semantics.asg.BinaryOperation.RelationalComparison;
import com.shapesecurity.shift.es2017.semantics.asg.Block;
import com.shapesecurity.shift.es2017.semantics.asg.BlockWithValue;
import com.shapesecurity.shift.es2017.semantics.asg.Break;
import com.shapesecurity.shift.es2017.semantics.asg.BreakTarget;
import com.shapesecurity.shift.es2017.semantics.asg.Call;
import com.shapesecurity.shift.es2017.semantics.asg.DeleteGlobalProperty;
import com.shapesecurity.shift.es2017.semantics.asg.DeleteProperty;
import com.shapesecurity.shift.es2017.semantics.asg.GlobalReference;
import com.shapesecurity.shift.es2017.semantics.asg.Halt;
import com.shapesecurity.shift.es2017.semantics.asg.IfElse;
import com.shapesecurity.shift.es2017.semantics.asg.Keys;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralBoolean;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralEmptyArray;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralEmptyObject;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralFunction;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralInfinity;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralNull;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralNumber;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralRegExp;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralString;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralSymbol;
import com.shapesecurity.shift.es2017.semantics.asg.LiteralUndefined;
import com.shapesecurity.shift.es2017.semantics.asg.LocalReference;
import com.shapesecurity.shift.es2017.semantics.asg.Loop;
import com.shapesecurity.shift.es2017.semantics.asg.MemberAccess;
import com.shapesecurity.shift.es2017.semantics.asg.MemberAssignment;
import com.shapesecurity.shift.es2017.semantics.asg.MemberAssignmentProperty;
import com.shapesecurity.shift.es2017.semantics.asg.MemberCall;
import com.shapesecurity.shift.es2017.semantics.asg.MemberDefinition;
import com.shapesecurity.shift.es2017.semantics.asg.New;
import com.shapesecurity.shift.es2017.semantics.asg.Node;
import com.shapesecurity.shift.es2017.semantics.asg.RequireObjectCoercible;
import com.shapesecurity.shift.es2017.semantics.asg.Return;
import com.shapesecurity.shift.es2017.semantics.asg.ReturnAfterFinallies;
import com.shapesecurity.shift.es2017.semantics.asg.SwitchStatement;
import com.shapesecurity.shift.es2017.semantics.asg.TemporaryReference;
import com.shapesecurity.shift.es2017.semantics.asg.This;
import com.shapesecurity.shift.es2017.semantics.asg.Throw;
import com.shapesecurity.shift.es2017.semantics.asg.TryCatch;
import com.shapesecurity.shift.es2017.semantics.asg.TryFinally;
import com.shapesecurity.shift.es2017.semantics.asg.TypeCoercionNumber;
import com.shapesecurity.shift.es2017.semantics.asg.TypeCoercionObject;
import com.shapesecurity.shift.es2017.semantics.asg.TypeCoercionString;
import com.shapesecurity.shift.es2017.semantics.asg.TypeofGlobal;
import com.shapesecurity.shift.es2017.semantics.asg.UnaryOperation.BitwiseNot;
import com.shapesecurity.shift.es2017.semantics.asg.UnaryOperation.Negation;
import com.shapesecurity.shift.es2017.semantics.asg.UnaryOperation.Not;
import com.shapesecurity.shift.es2017.semantics.asg.UnaryOperation.Typeof;
import com.shapesecurity.shift.es2017.semantics.asg.UnaryOperation.VoidOp;
import com.shapesecurity.shift.es2017.semantics.asg.VariableAssignment;
import com.shapesecurity.shift.es2017.semantics.asg.Void;
import javax.annotation.Nonnull;
package com.shapesecurity.shift.es2017.semantics.visitor;



public interface Reducer<State> {

	@Nonnull
	State reduceAll(@Nonnull Node node, @Nonnull State reduced);

	@Nonnull
	State reduceBlock(@Nonnull Block block, @Nonnull ImmutableList<State> children);

	@Nonnull
	State reduceLoop(@Nonnull Loop loop, @Nonnull State block);

	@Nonnull
	State reduceBreak(@Nonnull Break _break, @Nonnull State breakTarget);

	@Nonnull
	State reduceBreakTarget(@Nonnull BreakTarget breakTarget);

	@Nonnull
	State reduceCall(@Nonnull Call call, @Nonnull State callee, @Nonnull ImmutableList<State> arguments);

	@Nonnull
	State reduceLiteralUndefined(@Nonnull LiteralUndefined literalUndefined);

	@Nonnull
	State reduceLiteralSymbol(@Nonnull LiteralSymbol literalSymbol);

	@Nonnull
	State reduceLiteralInfinity(@Nonnull LiteralInfinity literalInfinity);

	@Nonnull
	State reduceLiteralRegExp(@Nonnull LiteralRegExp literalRegExp);

	@Nonnull
	State reduceLiteralEmptyArray(@Nonnull LiteralEmptyArray literalEmptyArray);

	@Nonnull
	State reduceLiteralEmptyObject(@Nonnull LiteralEmptyObject literalEmptyObject);

	@Nonnull
	State reduceLiteralFunction(@Nonnull LiteralFunction literalFunction, @Nonnull State body);

	@Nonnull
	State reduceReturn(@Nonnull Return _return, @Nonnull Maybe<State> expression);

	@Nonnull
	State reduceReturnAfterFinallies(@Nonnull ReturnAfterFinallies returnAfterFinallies, @Nonnull Maybe<State> savedValue);

	@Nonnull
	State reduceLiteralNull(@Nonnull LiteralNull literalNull);

	@Nonnull
	State reduceLiteralString(@Nonnull LiteralString literalString);

	@Nonnull
	State reduceLiteralNumber(@Nonnull LiteralNumber literalNumber);

	@Nonnull
	State reduceLiteralBoolean(@Nonnull LiteralBoolean literalBoolean);

	@Nonnull
	State reduceMemberCall(@Nonnull MemberCall memberCall, @Nonnull State object, @Nonnull State fieldExpression, @Nonnull ImmutableList<State> arguments);

	@Nonnull
	State reduceNot(@Nonnull Not not, @Nonnull State expression);

	@Nonnull
	State reduceBlockWithValue(@Nonnull BlockWithValue blockWithValue, @Nonnull State block, @Nonnull State result);

	@Nonnull
	State reduceFloatMath(@Nonnull FloatMath floatMath, @Nonnull State left, @Nonnull State right);

	@Nonnull
	State reduceIntMath(@Nonnull IntMath intMath, @Nonnull State left, @Nonnull State right);

	@Nonnull
	State reduceInstanceOf(@Nonnull InstanceOf instanceOf, @Nonnull State left, @Nonnull State right);

	@Nonnull
	State reduceNegation(@Nonnull Negation negation, @Nonnull State expression);

	@Nonnull
	State reduceEquality(@Nonnull Equality equality, @Nonnull State left, @Nonnull State right);

	@Nonnull
	State reduceNew(@Nonnull New expression, @Nonnull State callee, @Nonnull ImmutableList<State> arguments);

	@Nonnull
	State reduceThis(@Nonnull This expression);

	@Nonnull
	State reduceRequireObjectCoercible(@Nonnull RequireObjectCoercible requireObjectCoercible, @Nonnull State expression);

	@Nonnull
	State reduceTypeCoercionString(@Nonnull TypeCoercionString typeCoercionString, @Nonnull State expression);

	@Nonnull
	State reduceTypeCoercionNumber(@Nonnull TypeCoercionNumber typeCoercionNumber, @Nonnull State expression);

	@Nonnull
	State reduceTypeCoercionObject(@Nonnull TypeCoercionObject typeCoercionObject, @Nonnull State expression);