Simulate Macros to Simplify Scripts
- 
Scripts need to access existing data in variables. 
- 
The particular fields to access correspond to long/complex expressions (e.g. long indexing and/or property chains foo[x][y].bar[z].baz).
- 
Usage is prevalent inside the scripts, requiring extensive duplications of code that are prone to typos and errors. 
- 
There are a few such variables to modify at the same time – otherwise, it would be simpler to bind the thispointer to the variable.
- 
Pick a macro syntax that is unlikely to conflict with content in literal strings. 
- 
Before script evaluation/compilation, globally replace macros with their corresponding expansions. 
Pick a Macro Syntax
The technique described here is to simulate macros. They are not REAL macros.
Pick a syntax that is intuitive for the domain but unlikely to occur naturally inside string literals.
| Sample Syntax | Sample usage | 
|---|---|
| #FOO | #FOO = 42; | 
| $Bar | $Bar.work(); | 
| <Baz> | print(<Baz>); | 
| #HELLO# | let x = #HELLO#; | 
| %HEY% | %HEY% += 1; | 
Avoid normal syntax that may show up inside a string literal.
For example, if using Target as a macro:
// This script...
Target.do_damage(10);
if Target.hp <= 0 {
    print("Target is destroyed!");
}
// Will turn to this...
entities["monster"].do_damage(10);
if entities["monster"].hp <= 0 {
    // Text in string literal erroneously replaced!
    print("entities["monster"] is destroyed!");
}Global Search/Replace
// Replace macros with expansions
let script = script.replace("#FOO", "foo[x][y].bar[z].baz");
let mut scope = Scope::new();
// Add global variables
scope.push("foo", ...);
scope.push_constant("x", ...);
scope.push_constant("y", ...);
scope.push_constant("z", ...);
// Run the script as normal
engine.run_with_scope(&mut scope, script)?;print(`Found entity FOO at (${x},${y},${z})`);
let speed = #FOO.speed;
if speed < 42 {
    #FOO.speed *= 2;
} else {
    #FOO.teleport(#FOO.home());
}
print(`FOO is now at (${ #FOO.current_location() })`);