Skip to content

Declare the LaTeX2JS dialect explicitly, and fix the silent divergences behind it #27

Description

@pyramation

LaTeX2JS accepts a dialect of PSTricks that is partly a deliberate extension and partly accident, and today the two are indistinguishable. A document cannot say which one it is written in, so the extensions are invisible to a reader, unenforceable by a test, and impossible to tell apart from a bug.

This proposes making the dialect explicit and declared, fixing the accidents, and recording the remaining differences as decisions rather than surprises.

Every count below was measured against packages/latex2js/test/corpus (33 files) and verified against real PSTricks via tools/pstricks-conformance.


1. Declare the dialect

Add a \psset key, since \psset is already the scoped-settings mechanism:

\psset{dialect=latex2js}    % or: mathapedia

Default pstricks. A document that does not declare a dialect is read as PSTricks, and anything outside that spec is a diagnostic. The extensions stay fully supported — they just have to say so.

The point is signalling. Today \psplot[algebraic]{-4}{4}{pow(x,2)} looks like ordinary PSTricks and is not; under this change it either declares itself or gets told.

What the flag turns on

Construct PSTricks LaTeX2JS dialect
\userline, \uservariable, \slider do not exist 97 uses across 13 files — the interactive core, and the reason this project exists
plot bodies RPN PostScript (x x mul) infix (x^2), always
pow(a, b) no such function, only ^ 18 uses in 3 files
log(x) base 10 natural log — 4 uses in 1 file
plot bounds literal numbers expressions over variables — 18 uses
plotpoints=1 rejected, minimum is 2 one sample — 3 uses
bare option flags ([algebraic]) syntax error treated as true — 44 uses in 10 files
starred shapes (\psframe*) fill with linecolor honour fillcolor
colour names xcolor palette CSS names (lightblue) — 28 uses

15 corpus files need the flag. That refactor is mechanical and belongs in the same change as the feature, so the corpus proves the mechanism.

Migration

latex2js.com serves a checked-in bundle, so flipping the default breaks published content the moment it ships. Suggested sequencing:

  1. Ship the key with the diagnostic active but the old lenient behaviour intact, so nothing breaks and every affected document is named in the console.
  2. Flag the corpus and the site content.
  3. Flip the default to pstricks in the next major.

2. Silent wrongness — fix regardless of dialect

These are not extensions. They are inputs that produce a plausible wrong picture and no error, which is the pattern behind nearly every defect found in #26.

2.1 X/Y turn NaN into 0

packages/utils/src/index.ts — both transforms return 0 on a non-finite input after a console.warn. A NaN coordinate therefore becomes a real point at the origin, so a broken plot renders as a flat line pinned to the axis rather than failing.

\psplot{-2}{2}{x x mul} — a perfectly ordinary PSTricks body — does exactly this today.

This one masks the others. Worth doing first.

2.2 Three options parsed and ignored

Option Uses Consequence
arrowscale 28 arrowheads are a hardcoded var d = 8; 1.1× and 1.5× both render identically
plotstyle 3 plotstyle=dots draws nothing — the tangent-point dots are missing from every picture in graph.tex, and present in the PSTricks reference
showorigin 6 origin tick not suppressible (currently masked: all 6 uses pair it with labels=none)

2.3 Pi is undefined

MATH_FUNCTIONS defines pi, PI and π — but not Pi, which is pst-plot's own spelling. Pi evaluates to undefinedNaN → §2.1 turns it into a flat line. Verified against PSTricks, where Pi is 3.14159. Nothing in the corpus uses it yet, so this is latent. One line.

2.4 Unresolvable plot bodies should be diagnostics

The parser already carries a diagnostics channel with line and column. A plot body that yields no finite value should use it instead of drawing something.


3. Coverage gaps

  • \psecurve has zero corpus coverage. Implemented, never exercised by any example or stress case.
  • plotpoints=1 in graph.tex (3×) is invalid upstream. Meaningful in the LaTeX2JS dialect once §2.2 lands, so it needs the flag rather than a fix.

4. Decisions to record, not fix

These are genuine forks where the lenient reading is defensible. They should be written down in tools/pstricks-conformance/README.md and, where they are dialect behaviour, gated behind the flag.

  • Starred shapes honour fillcolor. Every corpus use passes one and plainly means it — the bar chart wants blue bars, not black.
  • log is natural log. graph.tex plots derivatives of 2^x, whose derivative is 2^x·ln(2). The author wrote log meaning ln, so LaTeX2JS draws the mathematically intended curve and PSTricks would draw the wrong one. Worth keeping — but as a declared choice.
  • CSS colour names, including green = #008000 where LaTeX's green is #00FF00.
  • Document typesetting: sections, theorems and equations are unnumbered; theorem headings are block rather than run-in; footnotes are inline superscripts. Numbering is the one worth revisiting, since it is what makes a document cross-referenceable.
  • psaxes labels default on (added in PSTricks semantic layer + conformance harness against real PSTricks #26 — changes 5 existing diagrams) and grid numbers opt-in (an SVG is clipped to the picture bounds, so an outside label would be invisible).

5. Latent trap

src/grammar/parser.js is copied verbatim by copy:grammar, not compiled. It is ES2015 against a repo targeting ES2020, so it is harmless today — but it bypasses tsc, so if the target is ever lowered to reach older browsers the parser will silently not be downlevelled and will become the floor, with nothing to indicate why. Either build it like everything else or emit it as ESM so tsc sees it.


Suggested order

  1. §2.1 — stop coercing NaN to 0. It hides everything else.
  2. §2.2, §2.3 — the ignored options and Pi. Small, visible, no design questions.
  3. §1 — the dialect key, plus flagging all 15 corpus files.
  4. §3psecurve coverage.
  5. §4 — write the decisions down.
  6. §5 — the build trap.

Steps 1–2 are strictly fixes and could land on their own. Step 3 is the one that needs agreement on the default and the migration window.

The conformance job added in #26 already guards this: every example is rendered with real PSTricks on each PR, and a picture that stops compiling fails the build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions