@wdprlib/ast 1.2.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -38,6 +38,7 @@ __export(exports_src, {
38
38
  link: () => link,
39
39
  lineBreak: () => lineBreak,
40
40
  italics: () => italics,
41
+ isTruthy: () => isTruthy,
41
42
  isStringContainerType: () => isStringContainerType,
42
43
  isParagraphSafe: () => isParagraphSafe,
43
44
  isHeaderType: () => isHeaderType,
@@ -45,6 +46,8 @@ __export(exports_src, {
45
46
  isAlignType: () => isAlignType,
46
47
  horizontalRule: () => horizontalRule,
47
48
  heading: () => heading,
49
+ formatExprValue: () => formatExprValue,
50
+ evaluateExpression: () => evaluateExpression,
48
51
  createSettings: () => createSettings,
49
52
  createPosition: () => createPosition,
50
53
  createPoint: () => createPoint,
@@ -250,6 +253,394 @@ function isParagraphSafe(element) {
250
253
  }
251
254
  // packages/ast/src/constants.ts
252
255
  var STYLE_SLOT_PREFIX = "\x00__IFTAGS_SLOT__";
256
+ // packages/ast/src/expr-eval.ts
257
+ var FALSE_VALUES = new Set(["false", "null", "", "0"]);
258
+ function isTruthy(value) {
259
+ return !FALSE_VALUES.has(value.toLowerCase().trim());
260
+ }
261
+ var MAX_EXPRESSION_LENGTH = 256;
262
+ function isTruthyNum(n) {
263
+ return n !== 0 && !Number.isNaN(n);
264
+ }
265
+ function formatExprValue(n) {
266
+ return String(n);
267
+ }
268
+ function evaluateExpression(expr) {
269
+ try {
270
+ if (expr.length > MAX_EXPRESSION_LENGTH) {
271
+ return { success: false, error: "expression too long" };
272
+ }
273
+ if (expr.trim() === "") {
274
+ return { success: false, error: "empty expression" };
275
+ }
276
+ const tokens = tokenize(expr);
277
+ if (tokens.length <= 1) {
278
+ return { success: false, error: "empty expression" };
279
+ }
280
+ const parser = new ExprParser(tokens);
281
+ const result = parser.parse();
282
+ if (!Number.isFinite(result)) {
283
+ return { success: false, error: "division by zero" };
284
+ }
285
+ return { success: true, value: result };
286
+ } catch (e) {
287
+ const msg = e instanceof Error ? e.message : "unknown error";
288
+ return { success: false, error: msg };
289
+ }
290
+ }
291
+ function tokenize(expr) {
292
+ const tokens = [];
293
+ let i = 0;
294
+ while (i < expr.length) {
295
+ const ch = expr[i];
296
+ if (/\s/.test(ch)) {
297
+ i++;
298
+ continue;
299
+ }
300
+ if (/\d/.test(ch) || ch === "." && /\d/.test(expr[i + 1] ?? "")) {
301
+ let numStr = "";
302
+ let hasDot = false;
303
+ while (i < expr.length) {
304
+ const c = expr[i];
305
+ if (c === ".") {
306
+ if (hasDot)
307
+ break;
308
+ hasDot = true;
309
+ } else if (!/\d/.test(c)) {
310
+ break;
311
+ }
312
+ numStr += c;
313
+ i++;
314
+ }
315
+ const num = parseFloat(numStr);
316
+ if (!Number.isFinite(num)) {
317
+ throw new Error("Invalid number");
318
+ }
319
+ tokens.push({ kind: "NUMBER", value: num });
320
+ continue;
321
+ }
322
+ if (/[a-zA-Z_]/.test(ch)) {
323
+ let id = "";
324
+ while (i < expr.length) {
325
+ const c = expr[i];
326
+ if (!/[a-zA-Z0-9_]/.test(c))
327
+ break;
328
+ id += c;
329
+ i++;
330
+ }
331
+ tokens.push({ kind: "IDENTIFIER", value: id.toLowerCase() });
332
+ continue;
333
+ }
334
+ if (ch === "<" && expr[i + 1] === "=") {
335
+ tokens.push({ kind: "LE", value: "<=" });
336
+ i += 2;
337
+ continue;
338
+ }
339
+ if (ch === ">" && expr[i + 1] === "=") {
340
+ tokens.push({ kind: "GE", value: ">=" });
341
+ i += 2;
342
+ continue;
343
+ }
344
+ if (ch === "!" && expr[i + 1] === "=") {
345
+ tokens.push({ kind: "NE", value: "!=" });
346
+ i += 2;
347
+ continue;
348
+ }
349
+ if (ch === "<" && expr[i + 1] === ">") {
350
+ tokens.push({ kind: "NE", value: "<>" });
351
+ i += 2;
352
+ continue;
353
+ }
354
+ if (ch === "!") {
355
+ tokens.push({ kind: "BANG", value: "!" });
356
+ i++;
357
+ continue;
358
+ }
359
+ switch (ch) {
360
+ case "+":
361
+ tokens.push({ kind: "PLUS", value: "+" });
362
+ break;
363
+ case "-":
364
+ tokens.push({ kind: "MINUS", value: "-" });
365
+ break;
366
+ case "*":
367
+ tokens.push({ kind: "STAR", value: "*" });
368
+ break;
369
+ case "/":
370
+ tokens.push({ kind: "SLASH", value: "/" });
371
+ break;
372
+ case "%":
373
+ tokens.push({ kind: "PERCENT", value: "%" });
374
+ break;
375
+ case "^":
376
+ tokens.push({ kind: "CARET", value: "^" });
377
+ break;
378
+ case "(":
379
+ tokens.push({ kind: "LPAREN", value: "(" });
380
+ break;
381
+ case ")":
382
+ tokens.push({ kind: "RPAREN", value: ")" });
383
+ break;
384
+ case ",":
385
+ tokens.push({ kind: "COMMA", value: "," });
386
+ break;
387
+ case "<":
388
+ tokens.push({ kind: "LT", value: "<" });
389
+ break;
390
+ case ">":
391
+ tokens.push({ kind: "GT", value: ">" });
392
+ break;
393
+ case "=":
394
+ tokens.push({ kind: "EQ", value: "=" });
395
+ break;
396
+ default:
397
+ throw new Error(`Unknown character: ${ch}`);
398
+ }
399
+ i++;
400
+ }
401
+ tokens.push({ kind: "EOF", value: "" });
402
+ return tokens;
403
+ }
404
+
405
+ class ExprParser {
406
+ tokens;
407
+ pos = 0;
408
+ constructor(tokens) {
409
+ this.tokens = tokens;
410
+ }
411
+ parse() {
412
+ const result = this.parseOr();
413
+ if (this.current().kind !== "EOF") {
414
+ throw new Error("too many values in the stack");
415
+ }
416
+ return result;
417
+ }
418
+ current() {
419
+ return this.tokens[this.pos] ?? { kind: "EOF", value: "" };
420
+ }
421
+ advance() {
422
+ const token = this.current();
423
+ this.pos++;
424
+ return token;
425
+ }
426
+ parseOr() {
427
+ let left = this.parseAnd();
428
+ while (this.current().kind === "IDENTIFIER" && this.current().value === "or") {
429
+ this.advance();
430
+ const right = this.parseAnd();
431
+ left = isTruthyNum(left) || isTruthyNum(right) ? 1 : 0;
432
+ }
433
+ return left;
434
+ }
435
+ parseAnd() {
436
+ let left = this.parseNot();
437
+ while (this.current().kind === "IDENTIFIER" && this.current().value === "and") {
438
+ this.advance();
439
+ const right = this.parseNot();
440
+ left = isTruthyNum(left) && isTruthyNum(right) ? 1 : 0;
441
+ }
442
+ return left;
443
+ }
444
+ parseNot() {
445
+ const cur = this.current();
446
+ if (cur.kind === "IDENTIFIER" && cur.value === "not" || cur.kind === "BANG") {
447
+ this.advance();
448
+ const value = this.parseNot();
449
+ return isTruthyNum(value) ? 0 : 1;
450
+ }
451
+ return this.parseComparison();
452
+ }
453
+ parseComparison() {
454
+ let left = this.parseAddition();
455
+ const kind = this.current().kind;
456
+ if (kind === "LT" || kind === "GT" || kind === "LE" || kind === "GE" || kind === "EQ" || kind === "NE") {
457
+ this.advance();
458
+ const right = this.parseAddition();
459
+ switch (kind) {
460
+ case "LT":
461
+ return left < right ? 1 : 0;
462
+ case "GT":
463
+ return left > right ? 1 : 0;
464
+ case "LE":
465
+ return left <= right ? 1 : 0;
466
+ case "GE":
467
+ return left >= right ? 1 : 0;
468
+ case "EQ":
469
+ return left === right ? 1 : 0;
470
+ case "NE":
471
+ return left !== right ? 1 : 0;
472
+ }
473
+ }
474
+ return left;
475
+ }
476
+ parseAddition() {
477
+ let left = this.parseMultiplication();
478
+ while (true) {
479
+ const kind = this.current().kind;
480
+ if (kind === "PLUS") {
481
+ this.advance();
482
+ left = left + this.parseMultiplication();
483
+ } else if (kind === "MINUS") {
484
+ this.advance();
485
+ left = left - this.parseMultiplication();
486
+ } else {
487
+ break;
488
+ }
489
+ }
490
+ return left;
491
+ }
492
+ parseMultiplication() {
493
+ let left = this.parsePower();
494
+ while (true) {
495
+ const kind = this.current().kind;
496
+ if (kind === "STAR") {
497
+ this.advance();
498
+ left = left * this.parsePower();
499
+ } else if (kind === "SLASH") {
500
+ this.advance();
501
+ left = left / this.parsePower();
502
+ } else if (kind === "PERCENT") {
503
+ this.advance();
504
+ left = left % this.parsePower();
505
+ } else {
506
+ break;
507
+ }
508
+ }
509
+ return left;
510
+ }
511
+ parsePower() {
512
+ const left = this.parseUnary();
513
+ if (this.current().kind === "CARET") {
514
+ this.advance();
515
+ const right = this.parsePower();
516
+ return Math.pow(left, right);
517
+ }
518
+ return left;
519
+ }
520
+ parseUnary() {
521
+ const kind = this.current().kind;
522
+ if (kind === "MINUS") {
523
+ this.advance();
524
+ return -this.parseUnary();
525
+ }
526
+ if (kind === "PLUS") {
527
+ this.advance();
528
+ return +this.parseUnary();
529
+ }
530
+ if (kind === "BANG") {
531
+ this.advance();
532
+ const value = this.parseUnary();
533
+ return isTruthyNum(value) ? 0 : 1;
534
+ }
535
+ return this.parsePrimary();
536
+ }
537
+ parsePrimary() {
538
+ const token = this.current();
539
+ if (token.kind === "NUMBER") {
540
+ this.advance();
541
+ return token.value;
542
+ }
543
+ if (token.kind === "LPAREN") {
544
+ this.advance();
545
+ const value = this.parseOr();
546
+ if (this.current().kind !== "RPAREN") {
547
+ throw new Error("Expected )");
548
+ }
549
+ this.advance();
550
+ return value;
551
+ }
552
+ if (token.kind === "IDENTIFIER") {
553
+ const name = token.value;
554
+ this.advance();
555
+ if (this.current().kind === "LPAREN") {
556
+ return this.parseFunctionCall(name);
557
+ }
558
+ if (name === "true")
559
+ return 1;
560
+ if (name === "false")
561
+ return 0;
562
+ throw new Error(`undefined constant "${name}"`);
563
+ }
564
+ throw new Error("Expected expression");
565
+ }
566
+ parseFunctionCall(name) {
567
+ if (this.current().kind !== "LPAREN") {
568
+ throw new Error("Expected (");
569
+ }
570
+ this.advance();
571
+ const args = [];
572
+ if (this.current().kind !== "RPAREN") {
573
+ args.push(this.parseOr());
574
+ while (this.current().kind === "COMMA") {
575
+ this.advance();
576
+ args.push(this.parseOr());
577
+ }
578
+ }
579
+ if (this.current().kind !== "RPAREN") {
580
+ throw new Error("Expected )");
581
+ }
582
+ this.advance();
583
+ return this.callFunction(name, args);
584
+ }
585
+ callFunction(name, args) {
586
+ switch (name) {
587
+ case "abs":
588
+ this.checkArgs(name, args, 1);
589
+ return Math.abs(args[0]);
590
+ case "min":
591
+ this.checkArgsMin(name, args, 1);
592
+ return Math.min(...args);
593
+ case "max":
594
+ this.checkArgsMin(name, args, 1);
595
+ return Math.max(...args);
596
+ case "floor":
597
+ this.checkArgs(name, args, 1);
598
+ return Math.floor(args[0]);
599
+ case "ceil":
600
+ this.checkArgs(name, args, 1);
601
+ return Math.ceil(args[0]);
602
+ case "round":
603
+ this.checkArgs(name, args, 1);
604
+ return Math.round(args[0]);
605
+ case "sqrt":
606
+ this.checkArgs(name, args, 1);
607
+ return Math.sqrt(args[0]);
608
+ case "sin":
609
+ this.checkArgs(name, args, 1);
610
+ return Math.sin(args[0]);
611
+ case "cos":
612
+ this.checkArgs(name, args, 1);
613
+ return Math.cos(args[0]);
614
+ case "tan":
615
+ this.checkArgs(name, args, 1);
616
+ return Math.tan(args[0]);
617
+ case "ln":
618
+ this.checkArgs(name, args, 1);
619
+ return Math.log(args[0]);
620
+ case "log":
621
+ this.checkArgs(name, args, 1);
622
+ return Math.log10(args[0]);
623
+ case "exp":
624
+ this.checkArgs(name, args, 1);
625
+ return Math.exp(args[0]);
626
+ case "pow":
627
+ this.checkArgs(name, args, 2);
628
+ return Math.pow(args[0], args[1]);
629
+ default:
630
+ throw new Error(`undefined function "${name}"`);
631
+ }
632
+ }
633
+ checkArgs(name, args, expected) {
634
+ if (args.length !== expected) {
635
+ throw new Error(`${name}() expects ${expected} argument(s), got ${args.length}`);
636
+ }
637
+ }
638
+ checkArgsMin(name, args, min) {
639
+ if (args.length < min) {
640
+ throw new Error(`${name}() expects at least ${min} argument(s), got ${args.length}`);
641
+ }
642
+ }
643
+ }
253
644
  // packages/ast/src/settings.ts
254
645
  function createSettings(mode) {
255
646
  switch (mode) {
@@ -259,7 +650,8 @@ function createSettings(mode) {
259
650
  enablePageSyntax: true,
260
651
  allowLocalPaths: true,
261
652
  useTrueIds: true,
262
- allowStyleElements: true
653
+ allowStyleElements: true,
654
+ allowHtmlBlocks: true
263
655
  };
264
656
  case "draft":
265
657
  return {
@@ -267,7 +659,8 @@ function createSettings(mode) {
267
659
  enablePageSyntax: true,
268
660
  allowLocalPaths: true,
269
661
  useTrueIds: false,
270
- allowStyleElements: false
662
+ allowStyleElements: false,
663
+ allowHtmlBlocks: false
271
664
  };
272
665
  case "forum-post":
273
666
  case "direct-message":
@@ -276,7 +669,8 @@ function createSettings(mode) {
276
669
  enablePageSyntax: false,
277
670
  allowLocalPaths: false,
278
671
  useTrueIds: false,
279
- allowStyleElements: false
672
+ allowStyleElements: false,
673
+ allowHtmlBlocks: false
280
674
  };
281
675
  }
282
676
  }
package/dist/index.d.cts CHANGED
@@ -1154,12 +1154,12 @@ declare const STYLE_SLOT_PREFIX = "\0__IFTAGS_SLOT__";
1154
1154
  * {@link WikitextSettings}. The modes correspond to the places where
1155
1155
  * user-authored wikitext can appear on a Wikidot site.
1156
1156
  *
1157
- * | Mode | Page syntax | Local paths | True IDs | Style elements |
1158
- * |--------------------|:-----------:|:-----------:|:--------:|:--------------:|
1159
- * | `"page"` | yes | yes | yes | yes |
1160
- * | `"draft"` | yes | yes | no | no |
1161
- * | `"forum-post"` | no | no | no | no |
1162
- * | `"direct-message"` | no | no | no | no |
1157
+ * | Mode | Page syntax | Local paths | True IDs | Style elements | HTML blocks |
1158
+ * |--------------------|:-----------:|:-----------:|:--------:|:--------------:|:-----------:|
1159
+ * | `"page"` | yes | yes | yes | yes | yes |
1160
+ * | `"draft"` | yes | yes | no | no | no |
1161
+ * | `"forum-post"` | no | no | no | no | no |
1162
+ * | `"direct-message"` | no | no | no | no | no |
1163
1163
  *
1164
1164
  * @group Settings
1165
1165
  */
@@ -1210,6 +1210,25 @@ interface WikitextSettings {
1210
1210
  * the CSS module is silently ignored.
1211
1211
  */
1212
1212
  allowStyleElements: boolean;
1213
+ /**
1214
+ * Whether `[[html]]` blocks are recognised by the parser and rendered.
1215
+ *
1216
+ * HTML blocks embed raw HTML that the renderer serves inside a sandboxed
1217
+ * iframe. The capability is meaningful only in contexts that can host
1218
+ * the auxiliary iframe URL, so it is disabled in drafts, forum posts,
1219
+ * and direct messages.
1220
+ *
1221
+ * When `false`, the parser still consumes the entire `[[html]]...[[/html]]`
1222
+ * span (so the raw body cannot leak as text) but emits no AST node, and
1223
+ * the renderer skips any pre-existing `html` element it encounters.
1224
+ *
1225
+ * Wikidot's legacy `Text_Wiki` keeps `Html` in its `$disable` list by
1226
+ * default (`lib/Text_Wiki/Text/Wiki.php` line 145-147), so an authentic
1227
+ * Wikidot-compat default would be `false` even in `"page"` mode. wp
1228
+ * keeps `"page"` at `true` for now to preserve existing consumers; a
1229
+ * future change may align with Wikidot.
1230
+ */
1231
+ allowHtmlBlocks: boolean;
1213
1232
  }
1214
1233
  /**
1215
1234
  * Create a {@link WikitextSettings} with sensible defaults for the given mode.
@@ -1232,6 +1251,51 @@ declare function createSettings(mode: WikitextMode): WikitextSettings;
1232
1251
  */
1233
1252
  declare const DEFAULT_SETTINGS: WikitextSettings;
1234
1253
  /**
1254
+ * Determine whether a string value is truthy for Wikidot's `#if` construct.
1255
+ *
1256
+ * The value is lowercased and trimmed before checking against the set of
1257
+ * known falsy strings (`"false"`, `"null"`, `""`, `"0"`).
1258
+ *
1259
+ * @param value - The condition string to check.
1260
+ * @returns `true` if the value is not in the falsy set.
1261
+ */
1262
+ declare function isTruthy(value: string): boolean;
1263
+ /**
1264
+ * Result of evaluating a mathematical expression.
1265
+ * Either a successful numeric value or an error message string.
1266
+ */
1267
+ type ExprResult = {
1268
+ success: true;
1269
+ value: number;
1270
+ } | {
1271
+ success: false;
1272
+ error: string;
1273
+ };
1274
+ /**
1275
+ * Format a numeric expression result for display.
1276
+ *
1277
+ * Uses JavaScript's default `String(n)` so the full precision of the
1278
+ * computed value is preserved (e.g. `1/3` becomes `"0.3333333333333333"`,
1279
+ * matching the `Number` → `String` conversion rather than truncating to
1280
+ * a fixed number of decimals). Used by both the inline renderer and the
1281
+ * opener preprocess so the same expression produces the same string
1282
+ * regardless of where it appears in the source.
1283
+ */
1284
+ declare function formatExprValue(n: number): string;
1285
+ /**
1286
+ * Evaluate a mathematical expression string and return the result.
1287
+ *
1288
+ * The expression is tokenized, parsed with a recursive descent parser,
1289
+ * and evaluated in a single pass. Errors produce Wikidot-compatible
1290
+ * messages (e.g., `"division by zero"`, `"too many values in the stack"`).
1291
+ *
1292
+ * NaN and Infinity results are treated as division-by-zero errors.
1293
+ *
1294
+ * @param expr - The expression string to evaluate.
1295
+ * @returns A success result with a numeric value, or an error result with a message.
1296
+ */
1297
+ declare function evaluateExpression(expr: string): ExprResult;
1298
+ /**
1235
1299
  * Identifies the source markup dialect.
1236
1300
  *
1237
1301
  * Currently only `"wikidot"` is supported. Included in {@link SyntaxTree}
@@ -1240,4 +1304,4 @@ declare const DEFAULT_SETTINGS: WikitextSettings;
1240
1304
  * @group Core
1241
1305
  */
1242
1306
  type Version = "wikidot";
1243
- export { text, paragraph, listItemSubList, listItemElements, list, link, lineBreak, italics, isStringContainerType, isParagraphSafe, isHeaderType, isContainerTypeParagraphSafe, isAlignType, horizontalRule, heading, createSettings, createPosition, createPoint, container, bold, WikitextSettings, WikitextMode, Version, VariableMap, UserData, TocEntry, TableRow, TableOfContentsData, TableData, TableCell, TabData, SyntaxTree, StringContainerType, STYLE_SLOT_PREFIX, Position, Point, ParseResult, PageRef, Module, MathInlineData, MathData, ListType, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LinkData, IncludeData, ImageSource, ImageData, IframeData, IfTagsData, IfExprData, IfCondData, HtmlData, HeadingLevel, Heading, HeaderType, FootnoteBlockData, FloatAlignment, ExprData, EmbedBlockData, Embed, ElementOf, ElementName, ElementDataMap, ElementData, Element, DiagnosticSeverity, Diagnostic, DefinitionListItem, DateItem, DateData, DEFAULT_SETTINGS, ContainerType, ContainerData, ColorData, CollapsibleData, CodeBlockData, ClearFloat, BibliographyCiteData, BibliographyBlockData, AttributeMap, AnchorTarget, AnchorData, Alignment, AlignType };
1307
+ export { text, paragraph, listItemSubList, listItemElements, list, link, lineBreak, italics, isTruthy, isStringContainerType, isParagraphSafe, isHeaderType, isContainerTypeParagraphSafe, isAlignType, horizontalRule, heading, formatExprValue, evaluateExpression, createSettings, createPosition, createPoint, container, bold, WikitextSettings, WikitextMode, Version, VariableMap, UserData, TocEntry, TableRow, TableOfContentsData, TableData, TableCell, TabData, SyntaxTree, StringContainerType, STYLE_SLOT_PREFIX, Position, Point, ParseResult, PageRef, Module, MathInlineData, MathData, ListType, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LinkData, IncludeData, ImageSource, ImageData, IframeData, IfTagsData, IfExprData, IfCondData, HtmlData, HeadingLevel, Heading, HeaderType, FootnoteBlockData, FloatAlignment, ExprResult, ExprData, EmbedBlockData, Embed, ElementOf, ElementName, ElementDataMap, ElementData, Element, DiagnosticSeverity, Diagnostic, DefinitionListItem, DateItem, DateData, DEFAULT_SETTINGS, ContainerType, ContainerData, ColorData, CollapsibleData, CodeBlockData, ClearFloat, BibliographyCiteData, BibliographyBlockData, AttributeMap, AnchorTarget, AnchorData, Alignment, AlignType };
package/dist/index.d.ts CHANGED
@@ -1154,12 +1154,12 @@ declare const STYLE_SLOT_PREFIX = "\0__IFTAGS_SLOT__";
1154
1154
  * {@link WikitextSettings}. The modes correspond to the places where
1155
1155
  * user-authored wikitext can appear on a Wikidot site.
1156
1156
  *
1157
- * | Mode | Page syntax | Local paths | True IDs | Style elements |
1158
- * |--------------------|:-----------:|:-----------:|:--------:|:--------------:|
1159
- * | `"page"` | yes | yes | yes | yes |
1160
- * | `"draft"` | yes | yes | no | no |
1161
- * | `"forum-post"` | no | no | no | no |
1162
- * | `"direct-message"` | no | no | no | no |
1157
+ * | Mode | Page syntax | Local paths | True IDs | Style elements | HTML blocks |
1158
+ * |--------------------|:-----------:|:-----------:|:--------:|:--------------:|:-----------:|
1159
+ * | `"page"` | yes | yes | yes | yes | yes |
1160
+ * | `"draft"` | yes | yes | no | no | no |
1161
+ * | `"forum-post"` | no | no | no | no | no |
1162
+ * | `"direct-message"` | no | no | no | no | no |
1163
1163
  *
1164
1164
  * @group Settings
1165
1165
  */
@@ -1210,6 +1210,25 @@ interface WikitextSettings {
1210
1210
  * the CSS module is silently ignored.
1211
1211
  */
1212
1212
  allowStyleElements: boolean;
1213
+ /**
1214
+ * Whether `[[html]]` blocks are recognised by the parser and rendered.
1215
+ *
1216
+ * HTML blocks embed raw HTML that the renderer serves inside a sandboxed
1217
+ * iframe. The capability is meaningful only in contexts that can host
1218
+ * the auxiliary iframe URL, so it is disabled in drafts, forum posts,
1219
+ * and direct messages.
1220
+ *
1221
+ * When `false`, the parser still consumes the entire `[[html]]...[[/html]]`
1222
+ * span (so the raw body cannot leak as text) but emits no AST node, and
1223
+ * the renderer skips any pre-existing `html` element it encounters.
1224
+ *
1225
+ * Wikidot's legacy `Text_Wiki` keeps `Html` in its `$disable` list by
1226
+ * default (`lib/Text_Wiki/Text/Wiki.php` line 145-147), so an authentic
1227
+ * Wikidot-compat default would be `false` even in `"page"` mode. wp
1228
+ * keeps `"page"` at `true` for now to preserve existing consumers; a
1229
+ * future change may align with Wikidot.
1230
+ */
1231
+ allowHtmlBlocks: boolean;
1213
1232
  }
1214
1233
  /**
1215
1234
  * Create a {@link WikitextSettings} with sensible defaults for the given mode.
@@ -1232,6 +1251,51 @@ declare function createSettings(mode: WikitextMode): WikitextSettings;
1232
1251
  */
1233
1252
  declare const DEFAULT_SETTINGS: WikitextSettings;
1234
1253
  /**
1254
+ * Determine whether a string value is truthy for Wikidot's `#if` construct.
1255
+ *
1256
+ * The value is lowercased and trimmed before checking against the set of
1257
+ * known falsy strings (`"false"`, `"null"`, `""`, `"0"`).
1258
+ *
1259
+ * @param value - The condition string to check.
1260
+ * @returns `true` if the value is not in the falsy set.
1261
+ */
1262
+ declare function isTruthy(value: string): boolean;
1263
+ /**
1264
+ * Result of evaluating a mathematical expression.
1265
+ * Either a successful numeric value or an error message string.
1266
+ */
1267
+ type ExprResult = {
1268
+ success: true;
1269
+ value: number;
1270
+ } | {
1271
+ success: false;
1272
+ error: string;
1273
+ };
1274
+ /**
1275
+ * Format a numeric expression result for display.
1276
+ *
1277
+ * Uses JavaScript's default `String(n)` so the full precision of the
1278
+ * computed value is preserved (e.g. `1/3` becomes `"0.3333333333333333"`,
1279
+ * matching the `Number` → `String` conversion rather than truncating to
1280
+ * a fixed number of decimals). Used by both the inline renderer and the
1281
+ * opener preprocess so the same expression produces the same string
1282
+ * regardless of where it appears in the source.
1283
+ */
1284
+ declare function formatExprValue(n: number): string;
1285
+ /**
1286
+ * Evaluate a mathematical expression string and return the result.
1287
+ *
1288
+ * The expression is tokenized, parsed with a recursive descent parser,
1289
+ * and evaluated in a single pass. Errors produce Wikidot-compatible
1290
+ * messages (e.g., `"division by zero"`, `"too many values in the stack"`).
1291
+ *
1292
+ * NaN and Infinity results are treated as division-by-zero errors.
1293
+ *
1294
+ * @param expr - The expression string to evaluate.
1295
+ * @returns A success result with a numeric value, or an error result with a message.
1296
+ */
1297
+ declare function evaluateExpression(expr: string): ExprResult;
1298
+ /**
1235
1299
  * Identifies the source markup dialect.
1236
1300
  *
1237
1301
  * Currently only `"wikidot"` is supported. Included in {@link SyntaxTree}
@@ -1240,4 +1304,4 @@ declare const DEFAULT_SETTINGS: WikitextSettings;
1240
1304
  * @group Core
1241
1305
  */
1242
1306
  type Version = "wikidot";
1243
- export { text, paragraph, listItemSubList, listItemElements, list, link, lineBreak, italics, isStringContainerType, isParagraphSafe, isHeaderType, isContainerTypeParagraphSafe, isAlignType, horizontalRule, heading, createSettings, createPosition, createPoint, container, bold, WikitextSettings, WikitextMode, Version, VariableMap, UserData, TocEntry, TableRow, TableOfContentsData, TableData, TableCell, TabData, SyntaxTree, StringContainerType, STYLE_SLOT_PREFIX, Position, Point, ParseResult, PageRef, Module, MathInlineData, MathData, ListType, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LinkData, IncludeData, ImageSource, ImageData, IframeData, IfTagsData, IfExprData, IfCondData, HtmlData, HeadingLevel, Heading, HeaderType, FootnoteBlockData, FloatAlignment, ExprData, EmbedBlockData, Embed, ElementOf, ElementName, ElementDataMap, ElementData, Element, DiagnosticSeverity, Diagnostic, DefinitionListItem, DateItem, DateData, DEFAULT_SETTINGS, ContainerType, ContainerData, ColorData, CollapsibleData, CodeBlockData, ClearFloat, BibliographyCiteData, BibliographyBlockData, AttributeMap, AnchorTarget, AnchorData, Alignment, AlignType };
1307
+ export { text, paragraph, listItemSubList, listItemElements, list, link, lineBreak, italics, isTruthy, isStringContainerType, isParagraphSafe, isHeaderType, isContainerTypeParagraphSafe, isAlignType, horizontalRule, heading, formatExprValue, evaluateExpression, createSettings, createPosition, createPoint, container, bold, WikitextSettings, WikitextMode, Version, VariableMap, UserData, TocEntry, TableRow, TableOfContentsData, TableData, TableCell, TabData, SyntaxTree, StringContainerType, STYLE_SLOT_PREFIX, Position, Point, ParseResult, PageRef, Module, MathInlineData, MathData, ListType, ListItem, ListData, LinkType, LinkLocation, LinkLabel, LinkData, IncludeData, ImageSource, ImageData, IframeData, IfTagsData, IfExprData, IfCondData, HtmlData, HeadingLevel, Heading, HeaderType, FootnoteBlockData, FloatAlignment, ExprResult, ExprData, EmbedBlockData, Embed, ElementOf, ElementName, ElementDataMap, ElementData, Element, DiagnosticSeverity, Diagnostic, DefinitionListItem, DateItem, DateData, DEFAULT_SETTINGS, ContainerType, ContainerData, ColorData, CollapsibleData, CodeBlockData, ClearFloat, BibliographyCiteData, BibliographyBlockData, AttributeMap, AnchorTarget, AnchorData, Alignment, AlignType };