What's the best way to separate logic from data, when some of the data *is* logic?

I'm working on a hobby project. For context, it's a Dungeons & Dragons 3.5e character generator.

For those who don't know, there's lot's of rules and options when generating the characters. End result is a Character which really just consists of a bunch of statistics (Stats). Some of these stats are numbers, others are information.

One of the steps during this process is selecting a "Class" for your character. "Barbarian" for example, or "Cleric". These classes determine a list of "Feats" that your character gets at different character levels.

The important part of all this is the "Feats". These feats can modify the character's data in many different ways, for example:

  • One feat will grant you Uncanny Dodge (which is itself a Feat), unless you already have Uncanny Dodge, in which case it gives you Improved Uncanny Dodge
  • Uncanny Dodge retains your DEX bonus to AC when flat-footed
    • Normally, flat-footed AC is calculated by adding a series of other stats together
  • Improved Uncanny Dodge has some other effects but has a prerequisite that you already have Uncanny Dodge
  • Another feat might grant you a +2 to SPOT checks, but only when such checks relate to a specific condition

To make sense of all this, I see:

  1. A feat's effect on a character can include IF/ELSE logic
    1. Feat that grants Uncanny Dodge if you don't already have it, or Improved Uncanny Dodge if you do
  2. A feat can grant you another feat
    1. Feat grants you Uncanny Dodge (or Improved Uncanny Dodge)
  3. A feat can change the logic used to calculate other stats
    1. Uncanny Dodge adds your DEX bonus to AC where it normally doesn't apply
  4. A feat can add a note to a stat, but not change the stat
    1. Add a note to the SPOT checks there there's an additional +2 under a specific condition

There are hundreds of Feats. I don't want to hard code them all, or rather, I could, but it seems madness. Instead, I want to keep them all in data files and load them that way.

But there's no consistent way to store the effects feats have in data files. I'm using standard CSV files at the moment, which leads me to:

(incomplete example)

Name Effect
Barbarian Uncanny Dodge IF Character.Feats.Contains(Uncanny Dodge) THEN ADD_FEAT Improved Uncanny Dodge ELSE Uncanny Dodge ENDIF
Uncanny Dodge Character.AC.FlatFooted += Character.DEX
Spotting Feat Character.Skills.SPOT.notes += "+2 when the moon is out"

I've no problem writing a tokeniser and parser for this; but the number of possibilities means I basically need to expose everything (stats and logic calculations) to manipulation via these options in the data files.

In my mind, I'm circling back to hard-coding everything and adding an even harder to use "programming language" to the data files. Sure, it would be useful to allow users to add their own custom Feats to my data files, but at that point if the user can do that, they could probably just download the source code and compile their own.

Maybe I could shift to Javascript or Lua in the files, but I'd still have to patch this together and expose data and logic; as well as adding Javascript and Lua libraries etc.

So what's the best way to do this? No matter which way I cut this, it doesn't seem sensible.

I originally posted this question https://www.reddit.com/r/CodingHelp/comments/1hzyluq/whats_the_best_way_to_separate_logic_from_data/ but I've a feeling this is a better subreddit for it.