diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp
index 1b169411fd..9deeeaed27 100644
--- a/src/scripting/codegeneration/codegen.cpp
+++ b/src/scripting/codegeneration/codegen.cpp
@@ -1550,7 +1550,7 @@ FxExpression *FxSizeAlign::Resolve(FCompileContext& ctx)
 	}
 	else
 	{
-		FxExpression *x = new FxConstant(Which == 'a' ? int(type->Align) : int(type->Size), Operand->ScriptPosition);
+		FxExpression *x = new FxConstant(Which == TK_AlignOf ? int(type->Align) : int(type->Size), Operand->ScriptPosition);
 		delete this;
 		return x->Resolve(ctx);
 	}
@@ -1811,12 +1811,12 @@ ExpEmit FxAssign::Emit(VMFunctionBuilder *build)
 	assert(ValueType == Base->ValueType && IsNumeric());
 	assert(ValueType->GetRegType() == Right->ValueType->GetRegType());
 
-	ExpEmit result = Right->Emit(build);
-	assert(result.RegType <= REGT_TYPE);
-
 	ExpEmit pointer = Base->Emit(build);
 	Address = pointer;
 
+	ExpEmit result = Right->Emit(build);
+	assert(result.RegType <= REGT_TYPE);
+
 	if (pointer.Target)
 	{
 		if (result.Konst)
diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp
index b2c58c1240..297ac2ef0b 100644
--- a/src/scripting/zscript/zcc_compile.cpp
+++ b/src/scripting/zscript/zcc_compile.cpp
@@ -2324,6 +2324,33 @@ FxExpression *ZCCCompiler::ConvertAST(ZCC_TreeNode *ast)
 	}
 }
 
+
+#define xx(a,z)	z,
+static int Pex2Tok[] = {
+#include "zcc_exprlist.h"
+};
+
+//==========================================================================
+//
+// Helper for modify/assign operators
+//
+//==========================================================================
+
+static FxExpression *ModifyAssign(FxBinary *operation, FxExpression *left)
+{
+	auto assignself = static_cast<FxAssignSelf *>(operation->left);
+	auto assignment = new FxAssign(left, operation);
+	assignself->Assignment = assignment;
+	return assignment;
+}
+
+
+//==========================================================================
+//
+// Convert an AST node and its children
+//
+//==========================================================================
+
 FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
 {
 	// Note: Do not call 'Simplify' here because that function tends to destroy identifiers due to lack of context in which to resolve them.
@@ -2426,11 +2453,11 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
 		{
 		case PEX_PostDec:
 		case PEX_PostInc:
-			return new FxPostIncrDecr(operand, op == PEX_PostDec ? TK_Decr : TK_Incr);
+			return new FxPostIncrDecr(operand, Pex2Tok[op]);
 
 		case PEX_PreDec:
 		case PEX_PreInc:
-			return new FxPreIncrDecr(operand, op == PEX_PostDec ? TK_Decr : TK_Incr);
+			return new FxPreIncrDecr(operand, Pex2Tok[op]);
 
 		case PEX_Negate:
 			return new FxMinusSign(operand);
@@ -2446,7 +2473,7 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
 
 		case PEX_SizeOf:
 		case PEX_AlignOf:
-			return new FxSizeAlign(operand, op == PEX_AlignOf ? 'a' : 's');
+			return new FxSizeAlign(operand, Pex2Tok[op]);
 
 		default:
 			assert(0 && "Unknown unary operator.");	// should never happen
@@ -2463,16 +2490,17 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
 		auto left = ConvertNode(binary->Left);
 		auto right = ConvertNode(binary->Right);
 		auto op = binary->Operation;
+		auto tok = Pex2Tok[op];
 		switch (op)
 		{
 		case PEX_Add:
 		case PEX_Sub:
-			return new FxAddSub(op == PEX_Add ? '+' : '-', left, right);
+			return new FxAddSub(tok, left, right);
 
 		case PEX_Mul:
 		case PEX_Div:
 		case PEX_Mod:
-			return new FxMulDiv(op == PEX_Mul ? '*' : op == PEX_Div ? '/' : '%', left, right);
+			return new FxMulDiv(tok, left, right);
 
 		case PEX_Pow:
 			return new FxPow(left, right);
@@ -2483,39 +2511,41 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
 		case PEX_BitAnd:
 		case PEX_BitOr:
 		case PEX_BitXor:
-			return new FxBinaryInt(op == PEX_LeftShift ? TK_LShift : op == PEX_RightShift ? TK_RShift : op == PEX_URightShift ? TK_URShift : op == PEX_BitAnd ? '&' : op == PEX_BitOr ? '|' : '^', left, right);
+			return new FxBinaryInt(tok, left, right);
 
 		case PEX_BoolOr:
 		case PEX_BoolAnd:
-			return new FxBinaryLogical(op == PEX_BoolAnd ? TK_AndAnd : TK_OrOr, left, right);
+			return new FxBinaryLogical(tok, left, right);
 
 		case PEX_LT:
 		case PEX_LTEQ:
 		case PEX_GT:
 		case PEX_GTEQ:
-			return new FxCompareRel(op == PEX_LT ? '<' : op == PEX_RightShift ? '>' : op == PEX_LTEQ ? TK_Leq : TK_Geq, left, right);
+			return new FxCompareRel(tok, left, right);
 
 		case PEX_EQEQ:
 		case PEX_NEQ:
-			return new FxCompareEq(op == PEX_NEQ ? TK_Neq : TK_Eq, left, right);
+			return new FxCompareEq(tok, left, right);
 
 		case PEX_Assign:
 			return new FxAssign(left, right);
-/*
-			case ZCC_MULEQ:
-			case ZCC_DIVEQ:
-			case ZCC_MODEQ:
-			case ZCC_ADDEQ:
-			case ZCC_SUBEQ:
-			case ZCC_LSHEQ:
-			case ZCC_RSHEQ:
-			case ZCC_ANDEQ:
-			case ZCC_OREQ:
-			case ZCC_XOREQ:
-				//break;
-			default:
-				Error(ast, "Invalid assign statement");
-*/
+
+		case PEX_AddAssign:
+		case PEX_SubAssign:
+			return ModifyAssign(new FxAddSub(tok, new FxAssignSelf(*ast), right), left);
+
+		case PEX_MulAssign:
+		case PEX_DivAssign:
+		case PEX_ModAssign:
+			return ModifyAssign(new FxMulDiv(tok, new FxAssignSelf(*ast), right), left);
+
+		case PEX_LshAssign:
+		case PEX_RshAssign:
+		case PEX_URshAssign:
+		case PEX_AndAssign:
+		case PEX_OrAssign:
+		case PEX_XorAssign:
+			return ModifyAssign(new FxBinaryInt(tok, new FxAssignSelf(*ast), right), left);
 
 			// todo: These do not have representations in DECORATE and no implementation exists yet.
 		case PEX_LTGTEQ:
diff --git a/src/scripting/zscript/zcc_exprlist.h b/src/scripting/zscript/zcc_exprlist.h
index 5c9936988c..4770a08a47 100644
--- a/src/scripting/zscript/zcc_exprlist.h
+++ b/src/scripting/zscript/zcc_exprlist.h
@@ -1,75 +1,75 @@
-// Name				n-ary
-xx(Nil,				)
+// Name				Token used in the code generator
+xx(Nil,				TK_None)
 
-xx(ID,				)
-xx(Super,			)
-xx(Null,			)
-xx(Self,			)
-xx(ConstValue,		)
-xx(FuncCall,		)
-xx(ArrayAccess,		)
-xx(MemberAccess,	)
-xx(TypeRef,			)
+xx(ID,				TK_Identifier)
+xx(Super,			TK_Super)
+xx(Null,			TK_Null)
+xx(Self,			TK_Self)
+xx(ConstValue,		TK_Const)
+xx(FuncCall,		'(')
+xx(ArrayAccess,		TK_Array)
+xx(MemberAccess,	'.')
+xx(TypeRef,			TK_Class)
 
-xx(PostInc,			)
-xx(PostDec,			)
+xx(PostInc,			TK_Incr)
+xx(PostDec,			TK_Decr)
 
-xx(PreInc,			)
-xx(PreDec,			)
-xx(Negate,			)
-xx(AntiNegate,		)
-xx(BitNot,			)
-xx(BoolNot,			)
-xx(SizeOf,			)
-xx(AlignOf,			)
+xx(PreInc,			TK_Incr)
+xx(PreDec,			TK_Decr)
+xx(Negate,			'-')
+xx(AntiNegate,		'+')
+xx(BitNot,			'~')
+xx(BoolNot,			'!')
+xx(SizeOf,			TK_SizeOf)
+xx(AlignOf,			TK_AlignOf)
 
-xx(Add,				)
-xx(Sub,				)
-xx(Mul,				)
-xx(Div,				)
-xx(Mod,				)
-xx(Pow,				)
-xx(CrossProduct,	)
-xx(DotProduct,		)
-xx(LeftShift,		)
-xx(RightShift,		)
-xx(URightShift,		)
-xx(Concat,			)
+xx(Add,				'+')
+xx(Sub,				'-')
+xx(Mul,				'*')
+xx(Div,				'/')
+xx(Mod,				'%')
+xx(Pow,				TK_MulMul)
+xx(CrossProduct,	TK_Cross)
+xx(DotProduct,		TK_Dot)
+xx(LeftShift,		TK_LShift)
+xx(RightShift,		TK_RShift)
+xx(URightShift,		TK_URShift)
+xx(Concat,			TK_DotDot)
 
-xx(LT,				)
-xx(LTEQ,			)
-xx(GT,				)
-xx(GTEQ,			)
-xx(LTGTEQ,			)
-xx(Is,				)
+xx(LT,				'<')
+xx(LTEQ,			TK_Leq)
+xx(GT,				'>')
+xx(GTEQ,			TK_Geq)
+xx(LTGTEQ,			TK_LtGtEq)
+xx(Is,				TK_Is)
 
-xx(EQEQ,			)
-xx(NEQ,				)
-xx(APREQ,			)
+xx(EQEQ,			TK_Eq)
+xx(NEQ,				TK_Neq)
+xx(APREQ,			TK_ApproxEq)
 
-xx(BitAnd,			)
-xx(BitOr,			)
-xx(BitXor,			)
-xx(BoolAnd,			)
-xx(BoolOr,			)
+xx(BitAnd,			'&')
+xx(BitOr,			'|')
+xx(BitXor,			'^')
+xx(BoolAnd,			TK_AndAnd)
+xx(BoolOr,			TK_OrOr)
 
-xx(Assign,			)
-xx(AddAssign,		)
-xx(SubAssign,		)
-xx(MulAssign,		)
-xx(DivAssign,		)
-xx(ModAssign,		)
-xx(LshAssign,		)
-xx(RshAssign,		)
-xx(URshAssign,		)
-xx(AndAssign,		)
-xx(OrAssign,		)
-xx(XorAssign,		)
+xx(Assign,			'=')
+xx(AddAssign,		'+')	// these are what the code generator needs, not what they represent.
+xx(SubAssign,		'-')
+xx(MulAssign,		'*')
+xx(DivAssign,		'/')
+xx(ModAssign,		'%')
+xx(LshAssign,		TK_LShift)
+xx(RshAssign,		TK_RShift)
+xx(URshAssign,		TK_URShift)
+xx(AndAssign,		'&')
+xx(OrAssign,		'|')
+xx(XorAssign,		'^')
 
-xx(Scope,			)
+xx(Scope,			TK_ColonColon)
 
-xx(Trinary,			)
+xx(Trinary,			'?')
 
-xx(Cast,			)
+xx(Cast,			TK_Coerce)
 
 #undef xx