Assuming you do, I suggest that you define a series of data types for things that can be manipuated or targeted, and formalize each possible spell effect as a function that takes one or more arguments of certain defined types and then does something to them and/or "returns" data of a particular type.
For example, types might include:
- Point (in space)
- Volume (of space)
- Creature
- Object
- Energy Type
- Event
You also have some things that work on sets of a particular type. So charm takes an argument of type Creature, but mass charm takes an argument of type Set of (Creatures).
Then, the caster needs some way to select inputs of appropriate types. To that end, he acquires functions like:
- Me(), which returns the creature casting the spell
- TouchedObject(), which returns an object that the caster is currently touching
- ViewedPoint(), which returns the point where the caster's eyes are currently focused
- EnclosedDrawing(), which returns the volume enclosed by a shape that the caster has drawn, carved, or otherwise marked the borders of (yes, this ignores the third dimension; magic circles usually do)
- LocationOfObject(object), which returns the point at the center of mass of the object that you pass in as an argument
- SphereCenteredOn(point), which returns the volume defined by a sphere centered around the specified point
- Fire(), which returns the energy type "fire"
Then the caster needs a library of functions that actually cause physical manifestations and accomplish stuff. These might include:
- EnergyBurst(energy type, point), which causes a manifestation of the specified energy type at the specified point. For example, EnergyBurst(Fire(), ViewedPoint()) creates fire whereever you're looking, or EnergyBurst(Positive(), LocationOfObject(Me())) generates a burst of positive energy centered on you.
- ApplyForce(object, point), which applies force on an object in the direction of a specified point. For example, ApplyForce(TouchedObject(), ViewedPoint()) sends whatever you're touching flying towards whatever you're looking at.
Triggered/contingent spells can use some sort of When(event, function) metafunction that triggers the specified effect when the specified event occurs. You can have similar metafunctions for repetition, delays, etc.
You can try to write this out as pseudo-English instead of explicit functions (so "EnergyBurst(Positive(), LocationOfObject(Me()))" becomes something like "burst of positive energy at the location of me"), but I think that's just going to encourage misunderstanding without really making things any easier or more flexible. Real computer languages do stuff like that, of course, but the computer always transforms it into an explicit hierarchical structure before executing it, so if the spells are going to be interpreted by human beings, you're probably just creating more work for the players.
Even with only this cursory examination, a few observations suggest themselves to me:
- You still need to explicitly define a fairly extensive list of spell effects. You can easily generalize things so that creating a fireball also gives you an iceball and a column of fire, and so that delayed blast fireball gives you delayed blast (anything you already have), but fireballs and telekinesis and teleportation and illusions and so forth are all fundamentally different effects, which can maybe share targeting and metamagic rules but each require completely independent support in the category of "functions that actually do stuff".
- You're going to have all the standard problems of metamagic, where you need to anticipate the strongest possible use and then set the cost based on that, making everything weaker than the strongest possible use too expensive to be worth it and creating an exploit every time you fail to identify the strongest possible use.
- Most of the interesting chaining options are going to require variables. For example, "when a creature enters this area, immolate that creature" requires that the creature reference in the second part is somehow known to be the same creature that was referenced in the first part. "In 5 rounds, create a bright light on the object I'm touching right now" requires that the object is determined now but then used in another effect 5 rounds later (lighting the object you're touching in 5 rounds would be a completely different spell). That's going to add a whole new layer of complexity.
My favoriate analogy for computer programming is that it's like bargaining with an evil genie: the computer will do exactly what you say, but if there is any possible way it can follow your instructions without achieving your goal, then you've failed. With computers, that's because it can't follow a vague instruction, rather than because it's applying malicious interpretation, but the required precision is the same. Though if your spells are going to be "executed" by the GM, your situation may actually be more like the evil genie.
So, imagine a game in which every spell you cast requires telling an evil genie what you want to have happen...