Parsers
Every tag the interpreter understands comes from a parser you registered.
A parser implements one tag, or a family of related tags. The interpreter has none of its own, so a fresh new Interpreter() renders plain text and leaves every {tag} exactly as written. What a template can do is precisely the list of parsers you pass in.
import { Interpreter, RandomParser } from 'tagscript';
const ts = new Interpreter(new RandomParser());
(await ts.run('{random:heads,tails}')).body; // 'tails'
(await ts.run('{if(1==1):yes|no}')).body; // '{if(1==1):yes|no}'Add or replace them later with ts.addParsers(...) and ts.setParsers(...).
Built-in parsers
Logic and control flow
| Page | Tags | What it does |
|---|---|---|
| If statement | if | Pick one of two messages from a comparison. |
| Union statement | any, or, union | True when any expression holds. |
| Intersection statement | all, and, intersection | True when every expression holds. |
| Stop | stop, halt, error | End the render and return a message. |
| Break | break | Replace the output but keep parsing. |
Variables
| Page | Tags | What it does |
|---|---|---|
| Variables | any seeded name | Read values your app supplied. |
| Define | =, assign, let, var | Name a value and reuse it later. |
| JSON | json | Turn a JSON payload into a named variable. |
Text
| Page | Tags | What it does |
|---|---|---|
| Formatting | lower, upper, capitalize, escape, ord | Change case, escape syntax, ordinals. |
| Includes | in, contain, index, lindex | Search text and report position. |
| Replace | replace | Swap one string for another. |
| Slice | slice, substr, substring | Cut out a substring. |
| URL encoding | urlencode, encodeuri, urldecode | Encode text for a URL. |
Randomness
| Page | Tags | What it does |
|---|---|---|
| Random | random, rand | Pick one item from a list. |
| Range | range, rangef | Pick a number between two bounds. |
| Fifty fifty | 5050, 50, ? | Render the payload on a coin flip. |
For Discord specific tags such as embed, cooldown and require, see the Discord plugin.
Writing your own
Implement IParser, or extend BaseParser to get tag name matching and the parameter and payload checks for free.
import { BaseParser, type Context, type IParser } from 'tagscript';
export class FetchParser extends BaseParser implements IParser {
public constructor() {
// accepted names, requires a parameter, requires a payload
super(['fetch'], false, true);
}
public async parse(ctx: Context) {
const response = await fetch(ctx.tag.payload!.trim());
return response.text();
}
}BaseParser takes three constructor arguments: the tag names it answers to, whether a parameter is required, and whether a payload is required. When a requirement is not met the parser declines the tag, and the interpreter leaves it in the output as written.
Both parse and willAccept may return a promise. parse returns the string that replaces the tag, or null to decline it after the fact, in which case the next parser that accepted the tag gets a turn.
A parser runs on text an untrusted author wrote. The FetchParser above will happily request any URL
the template names, including one on your internal network. Validate the payload before acting on it.
Recording an action instead of text
To ask the host app to do something, write to ctx.response.actions and return an empty string. The interpreter never acts on it, so your code stays in control of what actually happens.
import { BaseParser, type Context, type IParser } from 'tagscript';
declare module 'tagscript' {
interface IActions {
notify?: { channel: string };
}
}
export class NotifyParser extends BaseParser implements IParser {
public constructor() {
super(['notify'], true);
}
public parse(ctx: Context) {
ctx.response.actions.notify = { channel: ctx.tag.parameter! };
return '';
}
}Read response.actions.notify after the render and decide whether to honour it.
API reference
Last updated on