fix: Support \let via macros option (#3738)

* fix: Support `\let` via `macros` option

Issue #3737 turned out to be how we handled the return value of `expandOnce`.
We assumed that, if the return value isn't an `Array`, it's an
`instanceof Token`.  This isn't necessary true with a user's `macros`
object, and given that we don't currently export `Token`, it's pretty
difficult to bypass.

Given that we never actually use the array return values from
`expandOnce`, I changed the return value for `expandOnce` to either a
`number` (to indicate the number of expanded tokens, so you could still
look up the tokens in the stack if you wanted to) or `false`
(to indicate no expansion happened).  We can't use `0` for the latter
because an actual expansion might result in zero tokens.
The resulting code is arguably cleaner.

I also documented that `macros` can have object expansions, and
specified how to simulate `\let`.

Fixes #3737

* Revise macros documentation according to comments
This commit is contained in:
Erik Demaine
2023-04-17 15:59:56 -04:00
committed by GitHub
parent 62144e4abd
commit bdb0be2017
4 changed files with 44 additions and 24 deletions

View File

@@ -3499,6 +3499,20 @@ describe("A macro expander", function() {
expect`\futurelet\foo\frac1{2+\foo}`.toParseLike`\frac1{2+1}`;
});
it("macros argument can simulate \\let", () => {
expect("\\int").toParseLike("\\int\\limits", {macros: {
"\\Oldint": {
tokens: [{text: "\\int", noexpand: true}],
numArgs: 0,
unexpandable: true,
},
"\\int": {
tokens: [{text: "\\limits"}, {text: "\\Oldint"}],
numArgs: 0,
},
}});
});
it("\\newcommand doesn't change settings.macros", () => {
const macros = {};
expect`\newcommand\foo{x^2}\foo+\foo`.toParse(new Settings({macros}));