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][indexers] and/or [property][getters/setters] 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 `this` pointer 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 SyntaxSample 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:

```rust
// 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)?;

```js
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() })`);
```

After macro expansion, the _character positions_ of different script
elements will be shifted based on the length of the expanded text.

Therefore, error positions may no longer point to the correct locations
in the original, unexpanded scripts.

Line numbers are not affected.