Hyperflex

Developer's Guide — Story (Classic 2), 2026 Update

Gates • visits • hits • scoped variables • safe expression evaluation

1. Purpose

Hyperflex is the safe expression system used by Story (Classic 2) to decide whether page options are visible, active, hidden, or automatic. It evaluates visits, hit counts, page-family counts, saga-state variables, and qualified book/page references.

Primary engine/pzlm-scripts/Story (Classic 2)/js/HyperflexCompiler.js
Runtime integration/pzlm-scripts/Story (Classic 2)/js/ClassicStory.js
Page data/pzlm-assets/Story (Classic)/json/pages/<Book>/*.json

The compiler is deterministic and whitelist-parsed. It does not use eval() or Function().

2. Page JSON Format

{
  "book": "Puzzlum's Palace",
  "pageNum": 51,
  "title": "Balcony (1)",
  "illustration": "Story (Classic)/csv/sprites/YARD.csv",
  "illustrations": [
	"Story (Classic)/csv/sprites/BASE.csv",
	"Story (Classic)/csv/sprites/OVERLAY.csv"
  ],
  "desc": ["There is a courtyard below the", "balcony."],
  "logic": [
	["J", "Pond (1)", "+:Puzzlum's Palace (1)", "Jump"],
	["T", "Balcony (2)", "'-:Balcony (2)' && '@hasKey' === 1", "Talk to man"],
	["B", "@hasKey =: 1", "+:Cellar (1)", "Pick up brass key"]
  ]
}

illustration is the single-image form. illustrations is the ordered overlay form and renders first-to-last.

3. Logic Tuples

Each logic row is:

[ asciiKey, destinationTitle, prereqExpression, caption ]
IndexNameMeaning
0asciiKeyRequired. Empty string fails. A single literal space is valid for Space.
1destinationTitlePage title or special variable assignment destination.
2prereqExpressionRequired gate. Empty expression fails closed.
3captionDisplayed text. Blank caption creates a hidden/key-only binding if the gate passes.

4. Runtime Pipeline

  1. Normalize page JSON.
  2. Record current book/page visit and hit counts.
  3. Compile each logic row’s prerequisite expression.
  4. Evaluate against the current scoped story context.
  5. Bind hidden options when the gate passes and caption is blank.
  6. Render visible options when the gate passes and caption is non-blank.

Template-document features such as includes, loops, regex, and variable interpolation are pass-based and should resolve structure before final variable substitution.

includes → loops → conditionals → regex → variables / @@ → fixpoint cap

5. Expressions

Simple Gates

+:Balcony (2)	  // true if visited
-:Balcony (2)	  // true if not visited

Compound Gates

'+:Balcony (2)' && '-:Tower (1)'
"Dungeon Hall" >= 4
"@score" >= 10 && "@hasKey" === 1
"Puzzlum's Palace::@hasKey" === 1

Simple unquoted +: and -: gates may be auto-wrapped by ClassicStory. Compound expressions should quote page and variable tokens.

6. Identifiers

FormMeaningReturns
"+:Page Title"Visited checkBoolean
"-:Page Title"Unvisited checkBoolean
"Page Title (3)"Exact page hit countNumber
"Page Title"Base hit count across numbered variantsNumber
"@score"Current-book saga variableNumber/string
"Book::Page Title"Book-qualified page lookupBoolean/count
"Book::@score"Book-qualified variable lookupNumber/string

7. Operators

! + -
* / %
+ -
> >= < <=
== != === !==
&& ||

run(ctx) returns boolean gate truth. value(ctx) returns the raw evaluated value.

8. Variables and Scopes

Hyperflex now documents the full tier model for template contexts and Story contexts.

TierPurpose
localLoop/temporary context. Highest priority.
pageCurrent page context.
bookCurrent book context.
globalShared story/runtime context.
ink, nen, getEngine/site/request contexts when supplied.
local → page → book → global → vars

Bare names resolve through that order. Explicit names like page.title or book.hasKey resolve directly.

9. Indirect Variables with @@

Template expansion may use @@ to resolve a variable whose name is stored inside another variable.

 = title
{{ page::title }} = Miser's House
{{ @@ink::field }} → Miser's House

@@ belongs to the template-variable pass, after loops and conditionals have established the active local scope.

10. Regex Template Pass

Hyperflex template documents may pair pattern and replace variables for deterministic text transforms.

 = /\bcat\b/i
 = dog
{{ regex::ink::pattern::ink::replace::"The cat sat" }}
→ The dog sat

11. Debug Trace

Trace mode is read-only diagnostic output. It should report pass name, matched directive/expression, before/after summary, and skip/failure reason without changing render behavior.

Gate failExpression false, empty prereq, missing key, or parse error.
Scope missVariable/path not found in local/page/book/global.
Regex missBad pattern, missing replacement, or unchanged subject.
Fixpoint capTemplate kept changing until max-pass cutoff.

12. Compiler API

const hf = new HyperflexCompiler({ allowSingleQuotes: true });
const compiled = hf.compile("'@hasKey' === 1 && '+:Cellar (1)'");

compiled.ast;		// parsed AST
compiled.value(ctx); // raw value
compiled.run(ctx);   // boolean gate
const ctx = {
  isVisited(name) { return seenTitles.has(qualifyPage(name)); },
  getHits(name) { return exactHits.get(qualifyPage(name)) ?? 0; },
  getHitsBase(base) { return visits.get(qualifyBase(base)) ?? 0; },
  getVar(name) { return vars.get(qualifyVar(name)) ?? 0; },
  local: {}, page: {}, book: {}, global: {}, vars: new Map()
};

13. Recipes

Visit gate

"+:Doorway (1)"

One-time option

"-:Treasure Taken (1)"

Require region exploration

"Dungeon Hall" >= 4

Require a saga variable

"@hasKey" === 1

Cross-book state

"Puzzlum's Palace::@hasKey" === 1

14. Pitfalls

ProblemResultFix
Empty keyGate fails and option is not bound.Use a real key or literal Space.
Empty prereqFails closed.Use a valid always-true prerequisite.
Unquoted compound page namesParse error.Quote page tokens inside compound expressions.
Blank captionHidden/key-only binding.Use visible caption unless intentionally hidden.
Wrong book qualificationLookup misses.Use Book::Page or current-book local titles consistently.