diff --git a/ssc/ast/nodes.expressions.cs b/ssc/ast/nodes.expressions.cs
index 188e29e..a71d106 100644
--- a/ssc/ast/nodes.expressions.cs
+++ b/ssc/ast/nodes.expressions.cs
@@ -1,9 +1,14 @@
-using System.Collections;
+using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 
 namespace arookas {
-	class sunExpression : sunNode {
+	interface sunTerm {
+		sunExpressionFlags GetExpressionFlags(sunContext context);
+	}
+
+	class sunExpression : sunNode, sunTerm {
 		public sunExpression(sunSourceLocation location)
 			: base(location) { }
 
@@ -11,8 +16,11 @@ namespace arookas {
 			Stack<sunOperator> operatorStack = new Stack<sunOperator>(32);
 			CompileExpression(context, this, operatorStack);
 		}
+		public sunExpressionFlags Analyze(sunContext context) {
+			return AnalyzeExpression(context, this);
+		}
 
-		void CompileExpression(sunContext context, sunExpression expression, Stack<sunOperator> operatorStack) {
+		static void CompileExpression(sunContext context, sunExpression expression, Stack<sunOperator> operatorStack) {
 			// this implementation assumes that the expression production child list alternates between operand and operator
 			// we can safely assume this as the grammar "operand {binary_operator operand}" enforces it
 			int stackCount = operatorStack.Count;
@@ -47,6 +55,35 @@ namespace arookas {
 				operatorStack.Pop().Compile(context);
 			}
 		}
+		static sunExpressionFlags AnalyzeExpression(sunContext context, sunExpression expression) {
+			sunExpressionFlags flags = sunExpressionFlags.None;
+			foreach (var operand in expression.OfType<sunOperand>()) {
+				var term = operand.Term as sunTerm;
+				if (term != null) {
+					flags |= term.GetExpressionFlags(context);
+				}
+			}
+			return flags;
+		}
+
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return Analyze(context);
+		}
+	}
+
+	[Flags]
+	enum sunExpressionFlags {
+		// contents
+		None = 0,
+		Literals = 1,
+		Variables = 2,
+		Augments = 4,
+		Casts = 8,
+		Calls = 16,
+		Constants = 32,
+
+		// description
+		Dynamic = 64,
 	}
 
 	class sunOperand : sunNode {
@@ -71,7 +108,7 @@ namespace arookas {
 		}
 	}
 
-	class sunTernaryOperator : sunNode {
+	class sunTernaryOperator : sunNode, sunTerm {
 		public sunExpression Condition { get { return this[0] as sunExpression; } }
 		public sunExpression TrueBody { get { return this[1] as sunExpression; } }
 		public sunExpression FalseBody { get { return this[2] as sunExpression; } }
@@ -88,10 +125,14 @@ namespace arookas {
 			FalseBody.Compile(context);
 			context.Text.ClosePoint(trueEpilogue);
 		}
+
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return Condition.Analyze(context) | TrueBody.Analyze(context) | FalseBody.Analyze(context);
+		}
 	}
-	
+
 	// increment/decrement
-	class sunPostfixAugment : sunOperand {
+	class sunPostfixAugment : sunOperand, sunTerm {
 		public sunIdentifier Variable { get { return this[0] as sunIdentifier; } }
 		public sunAugment Augment { get { return this[1] as sunAugment; } }
 
@@ -108,9 +149,13 @@ namespace arookas {
 			}
 			Augment.Compile(context, symbol);
 		}
+
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return sunExpressionFlags.Augments;
+		}
 	}
 
-	class sunPrefixAugment : sunOperand {
+	class sunPrefixAugment : sunOperand, sunTerm {
 		public sunAugment Augment { get { return this[0] as sunAugment; } }
 		public sunIdentifier Variable { get { return this[1] as sunIdentifier; } }
 
@@ -127,6 +172,10 @@ namespace arookas {
 				symbol.CompileGet(context);
 			}
 		}
+		
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return sunExpressionFlags.Augments;
+		}
 	}
 
 	abstract class sunAugment : sunNode {
diff --git a/ssc/ast/nodes.functions.cs b/ssc/ast/nodes.functions.cs
index 80c7caf..2c230c9 100644
--- a/ssc/ast/nodes.functions.cs
+++ b/ssc/ast/nodes.functions.cs
@@ -27,7 +27,7 @@ namespace arookas {
 		}
 	}
 
-	class sunFunctionCall : sunNode {
+	class sunFunctionCall : sunNode, sunTerm {
 		public sunIdentifier Function { get { return this[0] as sunIdentifier; } }
 		public sunNode Arguments { get { return this[1] as sunNode; } }
 
@@ -47,6 +47,10 @@ namespace arookas {
 				context.Text.Pop();
 			}
 		}
+		
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return sunExpressionFlags.Calls | sunExpressionFlags.Dynamic;
+		}
 	}
 
 	class sunParameterList : sunNode {
diff --git a/ssc/ast/nodes.literals.cs b/ssc/ast/nodes.literals.cs
index 0e51cc7..a2693d4 100644
--- a/ssc/ast/nodes.literals.cs
+++ b/ssc/ast/nodes.literals.cs
@@ -3,7 +3,7 @@ using System.Globalization;
 using System.Text;
 
 namespace arookas {
-	class sunIntLiteral : sunToken<int> { // base-10 integer
+	class sunIntLiteral : sunToken<int>, sunTerm { // base-10 integer
 		public sunIntLiteral(sunSourceLocation location, string literal)
 			: base(location) {
 			Value = Int32.Parse(literal);
@@ -14,6 +14,10 @@ namespace arookas {
 			: base(location) { }
 
 		public override void Compile(sunContext context) { context.Text.PushInt(Value); }
+
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return sunExpressionFlags.Literals;
+		}
 	}
 
 	class sunHexLiteral : sunIntLiteral { // base-16 integer
@@ -31,7 +35,7 @@ namespace arookas {
 		}
 	}
 
-	class sunFloatLiteral : sunToken<float> {
+	class sunFloatLiteral : sunToken<float>, sunTerm {
 		public sunFloatLiteral(sunSourceLocation location, string literal)
 			: base(location) {
 			Value = Single.Parse(literal);
@@ -40,9 +44,13 @@ namespace arookas {
 		public override void Compile(sunContext context) {
 			context.Text.PushFloat(Value);
 		}
+
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return sunExpressionFlags.Literals;
+		}
 	}
 
-	class sunStringLiteral : sunToken<string> {
+	class sunStringLiteral : sunToken<string>, sunTerm {
 		public sunStringLiteral(sunSourceLocation location, string literal)
 			: base(location) {
 			Value = UnescapeString(literal.Substring(1, literal.Length - 2)); // remove enclosing quotes
@@ -123,6 +131,10 @@ namespace arookas {
 				(c >= 'A' && c <= 'F') ||
 				(c >= 'a' && c <= 'f');
 		}
+
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return sunExpressionFlags.Literals;
+		}
 	}
 
 	class sunIdentifier : sunToken<string> {
diff --git a/ssc/ast/nodes.system.cs b/ssc/ast/nodes.system.cs
index 705db09..04f3d9a 100644
--- a/ssc/ast/nodes.system.cs
+++ b/ssc/ast/nodes.system.cs
@@ -54,42 +54,46 @@
 		}
 	}
 
-	class sunIntCast : sunNode {
+	abstract class sunCast : sunNode, sunTerm {
 		public sunExpression Argument { get { return this[0] as sunExpression; } }
 
+		protected sunCast(sunSourceLocation location)
+			: base(location) { }
+
+		protected void Compile(sunContext context, sunBuiltinSymbol symbol) {
+			Argument.Compile(context);
+			context.Text.CallBuiltin(symbol.Index, 1);
+		}
+
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			return sunExpressionFlags.Casts | Argument.Analyze(context);
+		}
+	}
+
+	class sunIntCast : sunCast {
 		public sunIntCast(sunSourceLocation location)
 			: base(location) { }
 
 		public override void Compile(sunContext context) {
-			var builtinInfo = context.ResolveSystemBuiltin("int");
-			Argument.Compile(context);
-			context.Text.CallBuiltin(builtinInfo.Index, 1);
+			Compile(context, context.ResolveSystemBuiltin("int"));
 		}
 	}
 
-	class sunFloatCast : sunNode {
-		public sunExpression Argument { get { return this[0] as sunExpression; } }
-
+	class sunFloatCast : sunCast {
 		public sunFloatCast(sunSourceLocation location)
 			: base(location) { }
 
 		public override void Compile(sunContext context) {
-			var builtinInfo = context.ResolveSystemBuiltin("float");
-			Argument.Compile(context);
-			context.Text.CallBuiltin(builtinInfo.Index, 1);
+			Compile(context, context.ResolveSystemBuiltin("float"));
 		}
 	}
 
-	class sunTypeofCast : sunNode {
-		public sunExpression Argument { get { return this[0] as sunExpression; } }
-
+	class sunTypeofCast : sunCast {
 		public sunTypeofCast(sunSourceLocation location)
 			: base(location) { }
 
 		public override void Compile(sunContext context) {
-			var builtinInfo = context.ResolveSystemBuiltin("typeof");
-			Argument.Compile(context);
-			context.Text.CallBuiltin(builtinInfo.Index, 1);
+			Compile(context, context.ResolveSystemBuiltin("typeof"));
 		}
 	}
 
diff --git a/ssc/ast/nodes.variables.cs b/ssc/ast/nodes.variables.cs
index 5e9f6ee..2610150 100644
--- a/ssc/ast/nodes.variables.cs
+++ b/ssc/ast/nodes.variables.cs
@@ -1,5 +1,5 @@
 namespace arookas {
-	class sunStorableReference : sunNode {
+	class sunStorableReference : sunNode, sunTerm {
 		public sunIdentifier Storable { get { return this[0] as sunIdentifier; } }
 
 		public sunStorableReference(sunSourceLocation location)
@@ -8,6 +8,17 @@
 		public override void Compile(sunContext context) {
 			context.MustResolveStorable(Storable).Compile(context);
 		}
+
+		sunExpressionFlags sunTerm.GetExpressionFlags(sunContext context) {
+			var symbol = context.MustResolveStorable(Storable);
+			if (symbol is sunVariableSymbol) {
+				return sunExpressionFlags.Variables | sunExpressionFlags.Dynamic;
+			}
+			else if (symbol is sunConstantSymbol) {
+				return sunExpressionFlags.Constants;
+			}
+			return sunExpressionFlags.None;
+		}
 	}
 
 	class sunVariableDeclaration : sunNode {