Commit graph

152 commits

Author SHA1 Message Date
Yucong Sun
c5f3c3bdc7
docs: document call_out handle validity and mid-compile valid_read behavior (#1252)
Follow-up to reviewing PRs #1068 and #932, both of whose bugs are already
fixed on master:

- #1068 (call_out handles below the allocation counter rejected as
  invalid) was fixed by #1220 with a regression test in
  testsuite/single/tests/efuns/remove_call_out.lpc. Document the
  guaranteed semantics in call_out.md / remove_call_out.md: a handle
  stays valid until it fires or is removed, regardless of newer
  call_outs; 0 is never a valid handle.

- #932 (an error thrown from valid_read during #include handling left
  compile_file's reentrancy guard set, wedging all future loads) is
  fixed by the compiler front-end rewrite: compile_file's scope guard
  restores all compiler state and clears the guard on exception
  unwinding. Document in valid_read.md that the apply runs mid-compile
  for #include checks, must not trigger another compile, and that a
  thrown error aborts only that compilation.


Claude-Session: https://claude.ai/code/session_01JRvUnh3x5MYPWPk2MiiUS2

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-12 02:02:56 -04:00
Yucong Sun
814c4fa5b9
vm: string foreach/ref fixes, buffers as byte arrays (foreach, strict bytes, to_buffer), thorough ref tests; #1196 docs follow-up (#1250)
* docs/tests: fix constructs index regression from #1196; add ref page to sidebar; extend & ref tests

- Restore extension-less links and the text_blocks entry in
  docs/lpc/constructs/index.md (the PR was recreated from a pre-Docusaurus
  branch and reintroduced .html links, which fail the docs build under
  onBrokenLinks: 'throw', and dropped text_blocks)
- Add lpc/constructs/ref to the hand-authored sidebar in docs/sidebars.ts
- Drop the stale VitePress 'layout: doc' frontmatter from ref.md
- Extend testsuite/single/tests/operators/ref.lpc: & in parameter
  declarations, ref keyword in foreach, and bitwise &/&= non-regression

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Skf9CfHwAngWorDNDzPKWp

* vm: fix string foreach crash and off-by-one ref reads; support += / -= on string chars

Three related defects around the shared string-codepoint lvalue
(global_lvalue_codepoint), all reproduced on the unfixed binary:

- Nested foreach over strings SEGFAULTED: the iteration cursor lived in
  the shared global, so the inner loop's F_EXIT_FOREACH reset the
  iterator out from under the outer loop (null deref in
  post_index_to_offset). The EGC cursor now lives in each loop's own
  stack slot (the T_NUMBER slot under the loop variable); the ref case
  re-arms the shared codepoint lvalue every iteration, and exit only
  clears the global when it still points at this loop's string slot.

- foreach (int ref c in str) read the WRONG characters: the shared
  index was advanced before the body ran, so every read through the ref
  was off by one ('abc' summed to 197 instead of 294, and the final
  iteration read one past the end). The global index now stays on the
  current character for the whole body. Writes through the ref still go
  to the loop's by-value stack copy and never reach the iterated
  variable -- semantics pinned by tests/operators/foreach.lpc.

- s[i] += n / s[i] -= n threw "Bad Argument 1 to +=()": F_ADD_EQ and
  f_sub_eq handled buffer byte lvalues (T_LVALUE_BYTE) but not string
  codepoint lvalues (T_LVALUE_CODEPOINT), even though ++/--/= worked.
  Both now route through a new codepoint_lvalue_add() helper and produce
  the resulting character as the rvalue.

Regression tests: nested (plain / ref / multi-byte UTF-8) string foreach
and ref-read correctness in tests/operators/foreach.lpc; compound
assignment on string chars (incl. reverse index, rvalue result, and
non-number rhs error) in tests/operators/string_index.lpc. Verified on
the unfixed binary: the nested-foreach test segfaults, the others fail.
Full LPC suite passes 2x on clang ASan/UBSan Debug and 2x on
RelWithDebInfo.

Docs: correct the ref.md note on foreach-over-strings; document the
string-char lvalue rules in AGENTS.md (section 8 + audit checklist 9)
and the ref/& feature in README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Skf9CfHwAngWorDNDzPKWp

* vm: foreach ref over strings shares the s[i] char-lvalue logic; thorough ref tests

Arming a string-char lvalue now goes through one shared helper,
aim_lvalue_codepoint(): it validates the target EGC (out-of-bounds and
multi-codepoint error cleanly, same messages as s[i]) and points the
shared codepoint state at it. Both s[i] lvalues (push_indexed_lvalue)
and foreach ref loop variables use it, and every write consumer already
funnels into assign_lvalue_codepoint() -- so ref loop chars follow
exactly the s[i] rules:

- single-codepoint characters (and EGCs up to 4 bytes, which index as
  their first codepoint) keep working: reads deliver the character,
  assignments through the loop variable succeed
- wider EGCs (flag emoji, ZWJ sequences) now raise the catchable
  "Indexed character is multi-codepoint" error when the ref loop
  reaches them, instead of silently reading as -1; the non-ref form
  still iterates and delivers -1 for such clusters

tests/operators/ref.lpc is now a thorough pass-by-reference suite:
ref/& parameter declarations and call arguments, by-value contrast,
ref forwarding through call chains, call-site refs to array elements /
mapping values / string chars (forward and reverse index, write-back),
foreach ref over arrays / mapping values / strings (read correctness,
multi-byte codepoints, by-value write semantics, assignment error
parity, the multi-codepoint error, non-ref contrast), compile-time
rejections via generated sources (ref outside an argument list, ref to
a range), and bitwise &/&= non-regression -- 39 checks.

Full LPC suite passes 2x on RelWithDebInfo and 2x on a clean clang
ASan/UBSan Debug build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Skf9CfHwAngWorDNDzPKWp

* tests: cover ref across all LPC types

Extend tests/operators/ref.lpc to 58 checks: ref parameters of every
value type (int, float, string, array, mapping, object, function,
buffer, class, mixed) verifying reassignment propagates; the by-value
contrast for reference-typed containers (member writes propagate,
reassignment doesn't); call-site refs to buffer bytes (forward and
reverse index) and class members (both . and -> spellings); foreach
ref over mapping values of mixed types; the compile-time rejection of
a ref mapping KEY in foreach; and the clean runtime error for foreach
over a non-iterable type (buffer).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Skf9CfHwAngWorDNDzPKWp

* buffers behave like byte arrays: foreach, strict 0..255 bytes, string/array promotion, to_buffer()

Buffers are now first-class byte containers:

- foreach iterates a buffer like an array, delivering each byte as an
  unsigned int 0..255. A ref loop variable mutates the buffer in place;
  each ref carries its OWN T_LVALUE_BYTE (in ref->sv), so nested buffer
  ref loops and b[i] lvalues in the body can't alias each other.
  T_LVALUE_BYTE consumers now read the lvalue's own pointer/subtype
  instead of reaching for the shared global_lvalue_byte (which remains
  only as the scratch instance b[i] arms).

- every LPC byte write path (=, ++, --, +=, -=) range-checks the result:
  a value outside 0..255 raises "Buffer byte value out of range" and
  leaves the byte unchanged, instead of silently truncating/wrapping.
  += / -= on bytes also yield the resulting value as their rvalue.

- strings and arrays of ints 0..255 PROMOTE to buffers: a new
  to_buffer() efun (registered like to_int/to_float) is wrapped around
  the rhs by do_promotions() / rule_expr_assign for 'buffer b = str',
  'b += str', initializers, and 'b + str'; range assignment and the
  runtime + / += paths convert unpromoted (mixed) values through the
  same svalue_to_buffer_bytes() helper. A string contributes its raw
  UTF-8 bytes; an array must hold only ints 0..255 (validated before
  allocation) or the conversion errors with the target unchanged.

- fixed a pre-existing ref_t leak: a foreach ref loop variable reused by
  a re-entered inner loop leaked one ref per outer iteration (flagged by
  the debug memory checker as 'Found temporary block: make_ref').

tests/operators/buffer_bytes.lpc pins the byte-range semantics, + / +=
concatenation, all promotion forms, and the error paths (106 checks);
foreach.lpc and ref.lpc pin buffer iteration and ref-loop independence;
buffer_range_assign.lpc gains range-read pins. New docs for to_buffer
(sidebar regenerated) and a rewritten lpc/types/buffer page.

Full LPC suite passes 2x on RelWithDebInfo and 2x on a clean clang
ASan/UBSan Debug build (no ref-checker warnings); GTest suite 312/312.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Skf9CfHwAngWorDNDzPKWp

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-12 01:39:30 -04:00
gesslar
1ad5953549
docs: validate "See Also" references, drop dead ones, document valid_ffi (#1251)
* empty

* docs: validate "See Also" references, drop dead ones, document valid_ffi

Reviewed the "See Also" section of every doc and dropped man-page
cross-references whose target page does not exist (verified against the
driver source, not just the docs tree):

  - errorp, each, opcprof, dump_socket_status, extract, shadowp,
    destruct_env_of, move, inventory_visible, inventory_accessible

These name efuns/applies that no longer exist in FluffOS. keys/values now
point at the `for` construct in place of the defunct `each` efun.

valid_ffi was the one "referenced but undocumented" case: it is a real
master apply (APPLY_VALID_FFI) gating every ffi_load/symbol/prepare/
callback, so it gets a proper apply page rather than having its references
removed. Sidebar regenerated to include it.

The 13 modern markdown-link "See Also" sections were already clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:09:17 -04:00
gesslar
6e6d1c772b
docs: flesh out 31 TBW efun reference pages (#1248)
* empty

* docs: flesh out 31 TBW efun reference pages

Replace the placeholder "TBW" DESCRIPTION and type-only SYNOPSIS in 31
efun docs with real descriptions and named parameters, verified against
the driver source:

- math (general/): log10, log2, norm, dotprod, distance, angle
- matrix (general/): id_matrix, translate, scale, rotate_x/y/z,
  lookat_rotate, lookat_rotate2 -- note the in-place mutation of the
  passed matrix and that rotations are in degrees
- compress (general/): compress, uncompress, compress_file,
  uncompress_file -- note the file variants delete the source on success
- interactive/: send_zmp, act_mxp, request_term_type,
  start_request_term_type, request_term_size
- internals/: dump_trace, destructed_objects, check_memory (flag
  bitmask + DEBUGMALLOC build requirement), dump_stralloc, dump_jemalloc
- core: next_bit, explode_reversible, shallow_inherit_list

Each page keeps the existing manpage-style layout (4-space-indented
NAME/SYNOPSIS/DESCRIPTION, name(3) SEE ALSO). Example blocks are indented
to match, so all files are markdownlint-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 22:07:47 -04:00
Yucong Sun
89060c5a6b
docs: fully-expandable generated sidebar + Chinese docs via Docusaurus i18n (#1246)
* docs: fully-expandable generated sidebar, replacing index.md link pages

Rework docs navigation so the sidebar expands to every page of every
reference tree, instead of terminating at generated index.md link lists:

- New docs/gen_sidebar.py (replaces gen_index.py + update_index.sh):
  walks efun/, apply/, stdlib/, concepts/, driver/, cli/ and zh-CN/ and
  emits sidebars.generated.json — a full Docusaurus category tree per
  directory. Category landing pages are now `generated-index` card pages
  (title/description/slug), so all generated index.md files are deleted.
  --check mode verifies freshness; new .github/workflows/docs-sidebar.yml
  runs it in CI.
- New docs/sidebar_meta.json holds curated presentation: category labels,
  one-line descriptions (shown on the landing cards), explicit ordering
  (driver/cli/concepts read top-down from user-facing to internals) and
  per-page label overrides.
- sidebars.ts becomes a hand-authored skeleton (Getting Started, lpc/,
  Historical) that splices in the generated trees.

Content reorganization (from a docs-wide review):
- Move misplaced efun pages out of efun/general: terminal/protocol efuns
  (act_mxp, send_zmp, request_term_*) to interactive/, debugging efuns
  (check_memory, dump_*, clear_debug_level, destructed_objects) to
  internals/, shallow_inherit_list to system/.
- Delete stub duplicates superseded by complete pages elsewhere:
  general/parse_{add_synonym,dump,my_rules,remove}, contrib/{shuffle,
  element_of}.

Modernize key pages with MDX:
- index.mdx: landing page with a card grid linking each doc section.
- build.mdx: per-platform <Tabs> (Ubuntu/macOS/Windows/Alpine+Docker),
  admonitions, VitePress [[toc]] leftover removed, stale per-platform CI
  workflow links updated to the unified ci.yml.
- ffi-plan.md: GitHub-style [!CAUTION] alert converted to an admonition.

`npm run build` passes clean (onBrokenLinks: throw, no warnings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174GM2azvAHBmvyESwxm5om

* docs: serve the Chinese corpus through Docusaurus i18n

Move the zh-CN/ directory out of the default docs tree and into a proper
Docusaurus locale (i18n/zh-CN/docusaurus-plugin-content-docs/current/):

- The flat zh-CN/efun/ directory (333 pages) is re-homed to mirror the
  categorized English layout (name-matched 1:1; `hash` maps to strings/
  per its own frontmatter). apply/ pages map 1:1; the stray English-text
  zh-CN/apply/master/view_errors.md documents a MudOS-era apply that no
  longer exists in the driver and is dropped; stdlib/db/database_zh.md
  becomes the i18n translation of stdlib/db/database.md; the Chinese
  build guide becomes the translation of build.mdx.
- Untranslated pages automatically fall back to English content under
  /zh-CN/, so the whole site is navigable in either locale from the new
  navbar locale dropdown.
- Both locales share one sidebar. Generated sidebar items now carry
  stable `key` fields (the directory/doc path) so translation keys are
  unique (both efun/ and stdlib/ have an "Arrays" category, crypto and
  strings both document `hash`). Category labels, generated-index
  titles/descriptions, navbar and footer are translated in
  i18n/zh-CN/...; theme UI strings come from Docusaurus' bundled
  zh-Hans translations. Translated landing page at /zh-CN/.
- The "中文文档" sidebar section, the zh-CN tree in gen_sidebar.py /
  sidebar_meta.json, and its slice of sidebars.generated.json are gone.
- Relative .md-file links on pages that render in both locales break
  the localized build (the file->permalink map points at the localized
  copy), so concepts/, the two socket_*_option pages and the config.md
  generator now emit extension-less route links instead.
- zh interactive.md/objects.md get explicit slugs like their English
  counterparts (a doc named after its parent directory is otherwise a
  Docusaurus category-index doc, colliding with the generated-index
  route).

`npm run build` builds both locales clean (onBrokenLinks: throw).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174GM2azvAHBmvyESwxm5om

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-11 17:20:25 -04:00
Yucong Sun
5f7be1008f
Char-mode input: deliver real keystrokes (BS/DEL, whole UTF-8, raw ESC); fix NAWS lost at logon (#1245)
* Char-mode input: deliver real keystrokes (BS/DEL, whole UTF-8, raw ESC); fix NAWS lost at logon

Five input-path fixes that make raw-keystroke (get_char) applications
viable, found by building the LPC TUI library on top of them:

- comm.cc: char mode delivered "" for Backspace/Delete (the byte was
  zeroed before delivery, making BS/DEL/NUL indistinguishable). The
  literal byte is now delivered; line-mode in-buffer editing unchanged.
- comm.cc: char mode delivered one *byte* per callback, splitting a
  multi-byte UTF-8 character into 2-4 invalid one-byte strings.
  Extraction is now UTF-8 aware: a complete sequence arrives as one
  callback carrying one valid character; malformed bytes still go
  byte-at-a-time (no stalls).
- comm.cc: the "no ansi" + "strip before process input" ESC->space
  rewrite (both default on) also applied to char mode, so arrow keys
  arrived as literal "[A". The rewrite is an anti-ANSI-injection
  protection for line-mode commands; char mode now always passes ESC
  through.
- net/telnet.cc: each received chunk was u8_sanitize()d independently,
  so a UTF-8 character split across TCP segments became U+FFFD in any
  input mode. An incomplete trailing sequence (new u8_incomplete_tail(),
  GTest-covered) is now carried over in interactive_t and prepended to
  the next chunk.
- net/telnet.cc + comm.cc: fast clients answer the initial DO NAWS
  while ip->ob is still the master object, so the window_size apply
  fired on the wrong object and the size was lost until the next
  resize. The last report is cached and replayed on the user object at
  logon.

docs/efun/interactive/get_char.md documents the delivery contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

* Add /std/tui: an LPC TUI library (readline + ncurses for the prompt line and full-screen apps)

A terminal-UI toolkit in pure LPC, strictly layered (design + rationale
in testsuite/std/tui/DESIGN.md; user docs in docs/concepts/general/tui.md):

- ansi.lpc: escape builders plus the width toolkit the driver lacks
  (visible_width/wslice/wpad are ANSI-blind and wide-char aware).
- keys.lpc: keystroke decoder state machine: the get_char byte stream ->
  key events (CSI/SS3 with xterm modifiers, Alt prefixes, bracketed
  paste as one event, SGR mouse, UTF-8, lone-ESC via caller-driven
  flush()).
- readline.lpc: the line editor: emacs keymap (motion/kill/yank/
  transpose), Up/Down history, C-r/C-s incremental search, Tab
  completion, masked mode, horizontal scrolling with wide-char aware
  viewport; repaints only its own line so it works at any scroll
  position.
- screen.lpc: virtual cell grid + minimal-diff frame renderer with
  copy-on-write rows (frame cost tracks touched rows, not W x H);
  wide chars own two cells, boxes/fills/attrs as SGR param strings.
- widget.lpc + w/ (label, list, textfield) + app.lpc: widget protocol,
  focus cycling, event routing; textfield embeds a readline engine.
- terminal.lpc: the one impure module, inherited by the user object:
  get_char re-arm loop (I_NOECHO across re-arms), NAWS/TTYPE caching
  via the window_size/terminal_type applies, lone-ESC walltime timeout,
  guaranteed teardown. tui_readline() is a drop-in input_to()
  replacement; tui_open() runs full-screen apps on the alt screen.

Everything below terminal.lpc is a pure state machine: 194 checks in
single/tests/std/tui/ cover the decoder, editor sessions, history
search, screen diffing and widgets headlessly. `tuidemo` /
`tuidemo app` (command/tuidemo.lpc) demo both modes over any telnet
client; the whole stack was verified end-to-end over a live connection
including split-packet UTF-8, modifier keys, paste, resize and
alternate-screen teardown.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

* /std/tui v2: pterm/blessed-inspired widgets, inline printers and prompts, showcases

Reviewed the pterm (Go) and blessed widget catalogs and ported what earns
its keep in a MUD; DESIGN.md is renamed to README.md and documents the
full set (including what was deliberately not ported).

New layers:
- print.lpc — pterm-style printers that compose with plain write():
  p_table (boxed, width-aware, header rule), p_tree, p_bars, p_spark,
  p_panel, p_bullets, p_header, p_progress, p_info/success/warn/error,
  and p_bigtext via the existing /std/bitmap_font.
- menu.lpc + terminal glue — inline interactive prompts in the normal
  output flow: tui_select(), tui_multiselect() (Space toggles, list
  windows by height, collapses to a "? prompt: answer" line) and
  tui_confirm() (y/n with a default).

New widgets (/std/tui/w/): table (columns + header + selection), tree
(collapsible, arrows fold), checklist, radiolist, button, progress,
spinner (app-driven tick()), and log (bottom-anchored scrollback pane).

Showcases (command/tuidemo.lpc): `tuidemo select` (prompt chain),
`tuidemo print` (all printers), `tuidemo dashboard` (animated spinner,
progress bars, sparkline, live table and log on a call_out tick), and
`tuidemo form` (textfield, radio group, checkboxes, buttons).

Coverage: three new test files (print exact-output, menu sessions,
widgets2) bring /std/tui to 8 files in the suite; the live-connection
e2e run now drives all six showcases end-to-end (52 checks), including
the select->multiselect->confirm chain, unattended dashboard animation,
and full form entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

* /std/tui: charts — braille canvas, line charts, vertical bars, heatmap

Fills the chart gap left by v2 (only the horizontal p_bars existed):

- canvas.lpc: a braille dot canvas (each cell is a 2x4 dot grid from the
  U+2800 block — the blessed-contrib/drawille technique), giving sub-cell
  resolution: c_set/c_unset/c_get, Bresenham c_line, and c_plot (scale a
  value series across the canvas), with per-cell colour. Renders as a
  string, per-row strings, or per-cell ({ ch, attr }) pairs for blitting
  into a screen.
- print.lpc: p_chart (multi-series braille line chart with y-axis gutter
  and coloured legend), p_vbars (vertical bar chart with eighth-block
  partial tops and optional value row), p_heatmap (2D matrix as
  256-colour cells, cool-to-hot ramp, optional axis labels).
- w/chart.lpc: the live line-chart widget — add_series()/add_point()
  rolling history sized to the widget, auto or fixed y-range; replaces
  the dashboard's sparkline label with a real animating graph.
- `tuidemo charts` showcase; chart docs in README.md and the docs page
  (heatmap removed from the not-ported list).

Tests: single/tests/std/tui/charts.lpc pins the braille bit math with
exact glyphs, line drawing, plot endpoints, exact p_vbars output,
chart/heatmap structure, and the widget's rolling window. The live e2e
run verifies `tuidemo charts` output and braille frames streaming from
the dashboard chart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

* docs: move the TUI page from Concepts to STDLIB

/std/tui is a mudlib library like base64/break_string/json, so its page
belongs under docs/stdlib/ with the rest of the /std modules, not under
Concepts. Registered in stdlib/index.md; README file-layout pointer
updated. (Concepts' sidebar is autogenerated, so the old entry disappears
with the file.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpXv4yGCWpkzbkifyZy9EE

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-11 15:00:36 -04:00
Claude
b0f61c3313 Preserve this_player() in resolve() and async_* callbacks (#1104)
resolve(), async_read(), async_write(), async_getdir() and
async_db_exec() ran their callbacks with no user context, so
this_player() returned 0 and input_to()/printf() were unusable there.

Follow the call_out() precedent: capture command_giver (ref-held) when
the request is registered and restore it around the callback, skipping
destructed objects, gated on the same 'this_player in call_out'
setting. The pending-request holders account for the new ref in the
DEBUGMALLOC walkers.

The new test registers callbacks from two different living objects and
asserts each callback sees its own registrant, which fails without the
per-request capture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lv2Gw2AWqtyN3nNEgob1He
2026-07-11 14:56:46 -04:00
Claude
a136a45cd5 Add set_clean_up() efun: schedule an object's next clean_up query (#918)
Mirrors set_reset(): set_clean_up(ob, seconds) records an explicit
one-shot deadline that overrides the idle-time rule in the periodic
sweep; once it fires the object reverts to the idle rule. With the
seconds argument omitted, any pending deadline is cancelled. Both forms
re-flag the object for clean_up consideration (same gate as
request_clean_up(): the object must define a clean_up() apply).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lv2Gw2AWqtyN3nNEgob1He
2026-07-11 14:56:46 -04:00
Yucong Sun
6a25f5fe9b
wasm: shrink the driver to ~0.8MB over the wire (33.5MB -> 3.5MB raw) (#1243)
The wasm binary was dominated by the stock 30MB ICU data archive; the
driver only reads break-iterator data from it. Also drops zlib and the
last TLS reference from the target, and turns off MCCP/compress there.

- build-deps.sh trims the ICU archive with icupkg to brkitr rules + the
  converter alias table (~780KB). ICU_DATA_FILTER_FILE cannot do this:
  it only applies when building ICU data from source, and the -src
  tarball ships a prebuilt .dat. Table charsets (GBK, Big5, ...) are
  gone on this target -- string_encode() etc. raise an LPC error; a new
  __WASM__ LPC predefine lets mudlibs and tests adapt, and ICU_KEEP
  re-adds charsets for mudlibs that need them.
- zlib is not linked on wasm at all: a global HAVE_ZLIB (defined on
  every other platform) now gates the core's gzip'd file support --
  compressed save_object degrades to a plain save, write_file flag 2
  raises an error, and read_file/restore_object use stdio instead of
  transparent gzopen. That also surfaced a latent bug: core used gz*
  but only got zlib transitively via the compress package/libtelnet,
  so native now links ZLIB::ZLIB explicitly.
- TLS is fully gone from the target: the one shared caller of the TLS
  interface (the sys_reload_tls efun) is excluded from the wasm efun
  table in core.spec (the fullspec is preprocessed with the TARGET
  compiler, so #ifndef __EMSCRIPTEN__ works there), which lets the
  net/tls_stub.cc shim be deleted outright. Websocket code was already
  native-only via the Transport split.
- compress package + MCCP are off on wasm (compressing a byte stream to
  a client on the same page wastes CPU and size).
- INITIAL_MEMORY 128MB -> 64MB now that the data segment is small.
- Deps prefix is ICU-only; CI/release cache keys bumped to -v3 with the
  zlib pin removed. Docs (build-wasm.md guide, driver/wasm.md cookbook,
  src/wasm/README.md, README, AGENTS, sys_reload_tls efun page)
  updated.

Result: fluffos.wasm 33.5MB -> 3.5MB raw, ~0.8MB brotli / ~1.0MB gzip;
the full LPC testsuite passes inside the wasm driver.


Claude-Session: https://claude.ai/code/session_01VVpphH3cgXyziRDCbjUVkb

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-11 13:12:44 -04:00
Yucong Sun
82d82f3beb
Add recompile_object() efun: in-place program update, state preserved (#1237)
* Add recompile_object() efun: in-place program update, state preserved

Recompiles a master copy's program from its source file and swaps the
fresh program into the LIVE master copy and every clone sharing it -
the "hot update" alternative to destruct+load_object: nothing is
destructed, so object identity (pointers held elsewhere, name,
inventory, shadows, interactive state, call_outs, heart_beat) is
untouched, and each object's global variables carry over BY NAME
inside the driver (private ones included): the new program's __INIT
runs first, then every surviving name gets its old value back. The
recompile behaves like a normal load - unloaded parents resolve
through the retry dance and the compile-time master applies are
consulted. Returns the number of objects updated.

Made possible by moving an object's variable block OUT of the object_t
allocation into its own (TAG_OBJ_VARS, always >= 1 svalue, wired into
the debug-malloc walkers): every access already went through
ob->variables[i], so a program with a different variable count can now
be swapped onto a live object.

Safety: refused while any object sharing the program is executing
anywhere on the call stack (live frames hold bytecode positions and
variable indices of the old layout), for clones (pass the master
copy), the simul_efun object, pending replace_program(), and nested
calls. Function pointers whose behavior depends on the owner's program
layout (FP_LOCAL, FP_FUNCTIONAL) go stale instead of corrupting:
objects carry a prog_generation stamp, funptrs snapshot it at
creation/bind, and call_function_pointer() errors cleanly on mismatch.

Fixing a latent asymmetry this exposed: make_lfun_funp incremented
func_ref on the creation-time program but dealloc_funp decremented the
owner's CURRENT program. FP_LOCAL pointers now store their program and
account against it symmetrically (checkmemory and %O formatting
updated to match) - caught by the debug-build memory checker in the
testsuite.

The hot-reload daemon's default (state-keeping) path now reloads
through recompile_object() - changed ancestors first, then the watched
program - so clones ride along automatically; a cooperative
hot_reload_state()/hot_reload_restore() pair takes the destruct+load
path with exactly the state it chooses, and watch(prog, 0) opts out
entirely. The daemon test demonstrates finding all live instances with
children()/clonep() and both clone behaviors (updated in place vs.
stragglers on the old program); single/tests/efuns/recompile_object.lpc
pins the efun semantics (master+clones count, per-object state incl.
private, initializers for new variables, removed variables, stale
funptrs, executing/clone/missing-source guards, call_out survival).

Docs: efun reference page, hot-reload guide step 5 rewritten around
the efun with the value-transfer technique kept as the manual
alternative, caveats updated (clone behavior per path, stale funptrs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK

* recompile_object: support master/simul_efun targets; review fixes

The master object and the simul_efun object can now be recompiled
live. Both subsystems dispatch through cached name->runtime-index
tables (master_applies / simuls) whose entries point into the old
program's function table, so recompile_object() rebuilds them against
the new program immediately after the swap and BEFORE the new
program's __INIT runs (an error inside it would already route through
those tables). Simul_efun indices are preserved by NAME across the
rebuild - that table is deliberately unsorted for exactly this reason
- so simul calls compiled into every other program keep working, and
a simul removed by the new source fails with the usual "no longer a
simul_efun" runtime error. set_master()/set_simul_efun() only
ref/assign when the object actually changes, keeping the classic
destruct-driven replacement path intact. %O of a function pointer to
a since-removed simul now prints a placeholder instead of derefing
the null table entry.

Testsuite: the efun test recompiles the live simul_efun object
mid-run (the very next ASSERT dispatches through the rebuilt table),
pins the currently-executing guard on the master (master::flag() sits
on the call stack for the whole run), and re-runs the master recompile
from a post-run call_out where the master is idle - state carry-over
and apply dispatch are enforced by exiting nonzero.

Also from this self-review round (multi-agent):

* f_recompile_object crashed when the target destructed itself from
  its new program's __INIT: destruct sweeps the VM stack, so the efun
  glue's stack slot held a plain 0 by the time it tried to
  free_object() it. Reproduced by a review agent's probe; the glue
  now uses free_svalue(), and the scenario is pinned in the efun test
  (destructed targets drop out of the updated count).

* hot_reload daemon: ancestors() now returns the inherit closure
  DEEPEST-first - recompiling a middle parent bakes in whatever
  grandparent program is live at that moment, so a >=3-level chain
  with two changed ancestors permanently embedded the stale
  grandparent (reproduced by a review agent; pinned by a new
  kid/mid/grand scenario).

* hot_reload daemon: dep records were map_delete'd before the
  recompile and rebuilt by the applies during it - but a throw BEFORE
  compiling (currently-executing guard, unreadable file) left the
  object loaded with no records, blinding closure_changed() to
  include-file edits forever. Records are now restored when the
  recompile throws (pinned by a new watched-object-drives-the-pass
  scenario).

* docs: inheritance wording ("copies code" -> the child links against
  the exact parent program it was compiled with), the
  currently-executing guard also covers inheritors running inherited
  code, and the cooperative-pair opt-out triggers on
  hot_reload_state() alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK

* recompile_object: void mid-update replace_program; cover virtuals

Two additions from the C++ review round:

* A replace_program() registered DURING the update slipped past the
  pre-flight check: an earlier target's __INIT can call into a
  not-yet-swapped clone, whose OLD code registers a pending entry -
  computed against the very program the update is replacing. The
  backend sweep then ran that entry's variable-offset shuffle against
  the fresh program's differently-sized variable block (negative
  num_fewer, heap corruption; reproduced under ASan by a review
  agent's probe). recompile_object() now voids any pending entry for
  each target at its swap point - an entry registered AFTER the swap
  is computed against the new program and survives. Pinned in the
  efun test; the rest of the suite run doubles as the sweep detector.

* Virtual objects (materialized through master::compile_object) are
  covered and pinned: the virtual object carries the BACKING file's
  program, so the recompile targets that source and swaps it in with
  the virtual name, identity, flag and state untouched. The testsuite
  master gains a /data/hu/virt* fixture mapping; docs note the
  behavior and that the hot-reload daemon keys its records by
  compiled program name (watch virtuals via their backing file).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK

* docs: capture hot-reload/recompile_object knowledge in README and AGENTS

README: the hot-reload language bullet now describes what actually
ships (recompile_object with state carried by name, clones included),
and Features gains a Hot Reload section linking the guide.

AGENTS.md, for future agents working on this machinery: the object
variable block is a separate allocation (TAG_OBJ_VARS) and what that
enables; the new-DMALLOC-tag checklist (checkmemory walkers); the
destruct-sweeps-the-VM-stack rule for efun glue; testsuite harness
facts (fixtures outside tests/, unconditional teardown, master::flag
on the stack all run + the post-run call_out pattern, full -ftest
paths, suite side-effect files); the compile-time master applies; and
the recompile_object invariants (executing-frame guard, dispatch-table
rebuild before __INIT, voiding mid-update replace_program entries,
funptr generation staleness, FP_LOCAL func_ref symmetry).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK

* recompile_object: pin shadow/catch_tell/add_action/heart_beat survival

New test recompile_object2.lpc verifies the object-attached runtime
state that dispatches by name keeps working across the swap:
catch_tell routes into the new program while accumulated state stays;
a shadow chain survives updating the SHADOWED object (still
intercepted, new code underneath) and updating the SHADOW itself while
attached; add_action sentences registered by the old code still fire
their verb into the new program; the heart_beat registration persists.

Also two doc wording fixes from the docs review: the executing-guard
bullet now covers both halves of the guard (frames executing the
program's code AND frames belonging to an object of the program
running inherited code), and the guide's mode summary matches the
daemon (hot_reload_state alone selects the cooperative path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK

* recompile_object: fix simul_efun/__INIT edge cases; cover callback surface

Four defects from the C++ review round, each probe-verified under ASan:

* Recompiling the simul_efun object to a program that defines no simuls
  FREEd the live dispatch table (simul_names/simuls) while other
  compiled programs still carry F_SIMUL_EFUN opcodes and FP_SIMUL
  funptrs with baked indices -> use-after-free on the next simul call.
  Keep the tombstoned arrays instead (remove_simuls() already nulls
  every func, which yields the clean "no longer a simul_efun" error and
  preserves the name->index mapping for re-adds).

* The debug memory checker did not mark IHE_ORPHAN idents as permanent,
  so any run that removed a simul via an update tripped a spurious
  "orphan permanent identifier" leak and failed the testsuite gate.
  Add IHE_ORPHAN to the mark mask (it is part of IHE_PERMANENT).

* The disassembler dereferenced simuls[].func unguarded in two places;
  after a simul removal, dump_prog() on a program referencing it would
  null-deref. Guard both, matching the sprintf %O fix.

* An error() thrown from a target's __INIT during the swap leaked this
  loop's held references (the per-target snapshot ref, new_prog's
  compile ref, the old variable block) and left the update half-applied.
  Wrap call___INIT per target in save_context/try/restore: on error the
  object is left committed to the new program with fresh initializers
  (carried-over state dropped, like a create() that throws during load),
  sibling targets still update, and nothing leaks.

Test coverage:
* recompile_object.lpc: an __INIT that errors -- blueprint and clone
  both recompile, neither is immortalized, the object stays usable on
  the new program, no ref/variable leak (the per-file memory checker is
  the detector). (The simul zero-function / removal paths can't be
  exercised against the shared /single/simul_efun mid-suite; verified
  out-of-band with a throwaway ASan probe that reduces then restores the
  file.)
* recompile_object2.lpc: call_outs armed before the swap fire after it --
  a name-based call_out dispatches into the new program, a funptr
  call_out is stale and is refused cleanly (its target never runs, no
  crash), verified from a post-run call_out. Rounds out the by-name
  callback survivors already covered (catch_tell, add_action, heart_beat,
  shadows).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DGhzJfPhDGJA94EPh1gEK

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-11 11:24:30 -04:00
Claude
88b21f970b contrib: get_os_env()/set_os_env() with config allow-lists
LPC can now read and set OS environment variables -- the ask from the
dockerization discussion -- gated by two new colon-separated runtime
config allow-lists: 'allowed os environment variables' (readable) and
'writable os environment variables' (writable, implicitly readable).
Both default to empty, so nothing is accessible until the host
administrator opts specific names in. set_os_env(name) with no value
unsets. Windows uses _putenv_s.

Includes efun docs, index registration, regenerated config docs, and a
testsuite case exercising allow/deny/read/write/unset paths (the test
config allow-lists PATH read-only and FLUFFOS_TEST_RW read-write).

Fixes #1045

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FMBJLkpkpVpZdz6PWzeWe
2026-07-11 00:58:09 -04:00
Yucong Sun
e6f0c377f8
wasm: run the full driver in the browser (Emscripten port) (#1231)
The driver cross-compiles to WebAssembly and runs a complete mudlib
inside a webpage: compiler, VM, all portable efun packages, and the
real telnet protocol layer -- the page is the telnet client, JavaScript
is the wire. The LPC testsuite passes inside the wasm driver (browser
and node).

Architecture (src/wasm/README.md):
- Transport interface (src/net/transport.h): each interactive_t owns an
  abstract byte pipe (write/flush/schedule_command/close). comm.cc and
  telnet.cc are transport-agnostic and compile unchanged on every
  target. Implementations select at link time: SocketTransport +
  WebsocketTransport (net/transport_libevent.cc, native) vs
  WasmConsoleTransport (src/wasm/comm_wasm.cc).
- Inverted event loop: the shared gametick/event core stays in
  backend.cc; backend_libevent.cc (blocking loop) vs
  src/wasm/backend_wasm.cc (page-driven fluffos_tick(now_ms), walltime
  priority queue, capped catch-up). Other per-target singletons (TLS,
  DNS resolver, crash handler) follow the same link-time pattern -- no
  #ifdef __EMSCRIPTEN__ in shared logic files.
- Mudlib rides in Emscripten's MEMFS via file_packager; driver file I/O
  needed zero changes.

jsbridge package (WASM only): js_eval() (sync), js_call() (async page
handlers with LPC callbacks), js_export() (page calls LPC via
Module.fluffos.callLPC, Promise-resolved on a later tick) -- fetch,
canvas/WebGL, storage, page UIs driving the game. Demo in
testsuite/command/jsdemo.lpc + the bundled web terminal.

Build/tooling: native-tools + wasm CMake presets (host codegen tools,
then cross build); tools/wasm/build-deps.sh (ICU + zlib cross-builds,
including the ICU genccode data quirk); tools/wasm/pack-mudlib.sh
(any mudlib + driver -> static web bundle, also works standalone from
the release zip); src/www/wasm/index.html (self-contained web terminal
with a minimal telnet client). On emsdk >= 3.1.57 the build uses native
wasm exceptions (-fwasm-exceptions); older toolchains fall back to
-sDISABLE_EXCEPTION_CATCHING=0.

CI/release: a wasm CI job (latest emsdk, deps cache keyed on resolved
emcc version) gates PRs on the LPC testsuite running inside the wasm
driver under node; release.yml ships fluffos-<version>-wasm.zip
(driver + web terminal + pack-mudlib.sh). Fixed a stale-predefine bug:
options.autogen.h now depends on packages.autogen.h + config.h.

Docs: docs/build-wasm.md (end-to-end workflow), docs/driver/wasm.md
(packer + jsbridge cookbook), docs/efun/jsbridge/*, README + AGENTS
updated. Testsuite files for optional packages guard themselves with
#ifdef __PACKAGE_*__.


Claude-Session: https://claude.ai/code/session_01VVpphH3cgXyziRDCbjUVkb

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 23:33:51 -04:00
Claude
88fe45dd4d review fixes: test hygiene, lpcshell exit scope, dwlib CI coverage, doc nits
From an agent-based self-review of the branch:

- remove proto_sib1/proto_sib2.lpc, dead leftovers from an earlier
  repro iteration of the inherit_prototype test
- lpcshell: scope the nonzero-failure exit code and the evaluate-
  pending-at-EOF behavior to non-interactive runs, matching the
  documented intent (an interactive typo no longer turns Ctrl-D into
  exit 1)
- CI: build the optional dwlib package on the Ubuntu GCC legs so it
  cannot silently bit-rot again
- docs: alphabetize request_clean_up in the efun indexes and mark its
  argument optional in the synopsis; note in the explode gtest that
  the empty result is the static null array

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FMBJLkpkpVpZdz6PWzeWe
2026-07-10 10:26:58 -04:00
Claude
ae1baf9b24 docs: register request_clean_up in the efun indexes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FMBJLkpkpVpZdz6PWzeWe
2026-07-10 10:26:58 -04:00
Claude
75091675af core: member_array() flag 4 runs a function argument as a predicate
member_array(fp, arr, start, 4) now calls fp on each element and
returns the first index where it yields truthy, composing with the
existing prefix (1) and reverse (2) flag bits; without flag 4 a
function argument still matches by pointer identity as before.

Also fixes an adjacent bug: a reverse search (flag 2) that found
nothing returned sizeof(arr) instead of -1, because the found-index
mirroring was applied to the failure sentinel too.

Fixes #900

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FMBJLkpkpVpZdz6PWzeWe
2026-07-10 10:26:58 -04:00
Claude
64eabf810a core: new efun request_clean_up() to resume clean_up queries
Once clean_up() returns 0 the driver clears O_WILL_CLEAN_UP and never
asks that object again, with no way for LPC to opt back in. The new
efun re-arms the flag (for this_object() or a given object), returning
1 on success and 0 when the object defines no clean_up() apply --
mirroring the condition applied at load/clone time.

Fixes #917

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FMBJLkpkpVpZdz6PWzeWe
2026-07-10 10:26:58 -04:00
Yucong Sun
887f9ebbd1
docs: document UTF-8 native strings in the LPC language reference (#1226)
Add a 'UTF-8 Native Strings' section to lpc/types/strings.md covering
what the driver actually implements: lengths and positions are measured
in extended grapheme clusters (UAX #29), indexing yields code points and
errors on multi-code-point clusters, ranges/explode/strsrch operate on
character boundaries, display width (strwidth, UAX #11) vs length,
\uXXXX and surrogate-pair escapes, UTF-8 validity requirements, and the
encoding boundary (set_encoding for connections, string_encode /
string_decode / buffer_transcode elsewhere). Note in the old
sub-ranging section that positions are characters, not bytes.

Clarify sizeof() (string = grapheme clusters, buffer = bytes) and
strsrch() (character offsets, character-boundary matches).

Every documented example is pinned by a new testsuite file,
testsuite/single/tests/compiler/utf8_doc_examples.lpc, verified against
a freshly built driver (19 checks). Notably replace_string() is
byte-oriented, so it is deliberately NOT listed among the
grapheme-aware operations.


Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-10 07:42:20 -04:00
Yucong Sun
c914f03d66
Add local search and documentation guide for docs site (#1221)
* docs: add local full-text search and a contributor README

Add @easyops-cn/docusaurus-search-local to the Docusaurus site so the
docs get an offline search bar (index built at build time, no external
service). English and zh-CN pages are both indexed, and matched terms
are highlighted on the target page.

Add docs/README.md describing the Docusaurus setup, local dev/build
commands, search behavior, directory layout, and gotchas; exclude it
from the published site alongside CLAUDE.md. Point the root README's
docs/ entry at it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE

* docs: remove dead framework leftovers, fix index generation, complete the nav

Delete the VitePress (.vitepress/) and Jekyll (_layouts/, css/) leftovers,
the one-shot migration scripts (fix_md_header.py, fix_seealso.py), and the
stale keywords.json snapshot; prune the matching .gitignore entries and
docusaurus exclude patterns.

Rewrite gen_index.py for Docusaurus: it emitted dead .html links and
legacy 'layout: doc' frontmatter, choked on non-markdown entries, and
dropped nested categories — regenerating an index would have broken it.
It now emits the extension-less links the site actually uses, links
nested category indexes (restoring apply/* on the zh-CN index), and
refuses to run on the docs root. Fix update_index.sh's copy-paste titles
(zh-CN efun/build were titled 'APPLY'), stop it clobbering the
hand-written lpc/index.md, and cover cli/. Regenerated indexes pick up
the missing driver/ffi-plan entry. add_missing_efuns.py now takes the
keywords.json path as an argument instead of requiring a stale copy.

Move CNAME and the Google site-verification file into static/ so they
actually reach the published build output.

Complete the sidebar: link the CLI category to cli/index and add the
missing portbind/symbol/generate_keywords pages, and expose the
previously orphaned stdlib section under Reference.

Promote onBrokenLinks to 'throw' now the build is warning-free, and drop
the empty Demo section from the landing page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE

* docs: strip legacy 'layout: doc' frontmatter from all pages

Mechanical sweep removing the Jekyll-era 'layout: doc' line from every
doc page's frontmatter (Docusaurus ignores it), and the matching line
from the templates in docs/CLAUDE.md so new pages don't reintroduce it.
No content changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSzcESzU9947zkGzQ6SMmE

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-09 22:09:55 -04:00
Yucong Sun
c9b5f2f0eb docs/efun: document FFI + dwlib efuns, fix return-type errors, drop stale docs
Reviewed every efun doc against the .spec source of truth (the make_func
declarations) and the implementations.

New docs for 28 previously-undocumented efuns:
- package_ffi (18): new docs/efun/ffi/ category — ffi_load/unload/symbol/
  prepare/call, alloc/free/sizeof/peek/address/read/write, struct_layout,
  callback/callback_addr/callback_free, error/status.
- package_dwlib (10, under contrib/): add_a, vowel, replace,
  replace_dollars/html/mxp/objects, roulette_wheel, query_multiple_short,
  reference_allowed.

Return-type fixes where the doc contradicted the spec/impl (mostly stale
MudOS behavior): cache_stats/debugmalloc/dump_file_descriptors/
malloc_status/mud_status return string; disable_commands/flush_messages/
receive/set_heart_beat are void; get_char/input_to/link/set_eval_limit
return int; call_stack -> mixed *, function_profile -> mapping *. Also
corrected receive's and disable_commands' stale "returns ..." prose.

Expanded implode (function-fold form) and save_object (save-to-string
form), both of which the docs had omitted.

Removed 4 stale docs for efuns that no longer exist (no spec entry, no
f_ implementation, zero references anywhere): errorp, opcprof, swap,
dump_socket_status.

Regenerated the affected efun index pages. The Docusaurus build passes
with onBrokenLinks:throw, confirming no dangling links.

Note: the docs/zh-CN translated tree still mirrors the old English docs
(same stale entries, and it lacks the new efuns); left for a separate
translation pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 20:48:48 -04:00
Yucong Sun
48fe0e9b5a Grammar-driven lexical simplification; minimal token inventory
Lexical decisions move from lexer state into the grammar, where LALR
lookahead already disambiguates:
- Array/mapping opens ({ / ([ are ordinary '(' '{' / '(' '[' token
  pairs the grammar pairs (composite tokens deleted).
- The whole '(: name' first-class-function machinery (dedicated start
  condition, function_flag, one-byte peek, old_func()) becomes two
  grammar productions; %expect documents the intentional conflicts.
- Token diet: dead tokens deleted; single-char operators are plain char
  tokens ('!', '.'); same-precedence families share one value-carrying
  token (L_EQ_NE, L_SHIFT, L_INC_DEC -- the L_ORDER idiom). Release-
  build illegal-char diagnostics made unconditional; CRLF multi-line
  #define fold pinned (Windows).

Includes the merge of current master (docs-only advance).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:48:48 -04:00
Yucong Sun
2063e95436
Fix Docusaurus sidebar, broken links, and gh-pages CI (#1209)
* Reorder sidebar: Driver > CLI > Reference (LPC Language, Apply, EFUN, Concepts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix Docusaurus build: broken links, duplicate routes, and gh-pages CI

- Strip .html from all markdown link targets (51 files) for Docusaurus URL routing
- Add slug: frontmatter to 4 files whose names match their parent directory
  (interactive.md, objects.md, README.md, build.md) to prevent Docusaurus's
  category-index convention from creating duplicate routes
- Fix one missed .html link in zh-CN/build/index.md
- Move onBrokenMarkdownLinks to markdown.hooks (Docusaurus v4 deprecation)
- Update gh-pages.yml: rename to Docusaurus, use node 22, correct build path
  (docs/build instead of docs/.vitepress/dist)

Build now completes with [SUCCESS] and zero warnings or broken links.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 21:01:21 -07:00
Yucong Sun
1ee613bd15 Migrate docs from VitePress to Docusaurus 3.10.1
The package.json had dead Docusaurus deps from a previous setup while the
site itself was VitePress. This completes the migration back to Docusaurus
3.10.1 with its latest theme and features:

- Add docusaurus.config.ts with docs plugin (path: '.', routeBasePath: '/')
  and markdown.format: 'detect' so .md files use standard markdown
- Add sidebars.ts converted from the VitePress sidebar.ts format
- Replace devDependency-only vitepress with full Docusaurus preset-classic
- Upgrade clsx 1.x → 2.x, prism-react-renderer 2.1 → 2.4, typescript 5.2 → 5.8
- Update index.md to add slug: / for the home page
- Escape {…} in 7 doc files where curly braces appeared in prose text and
  were mis-parsed as JSX expressions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 21:33:42 -07:00
gesslar
053711e685
Fix max_eval_cost() return type in docs: void → int (#1193)
The synopsis incorrectly listed `void max_eval_cost()` but the function
returns an int (the max eval cost). The spec, implementation, description,
and zh-CN translation all agree on `int`.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-23 21:07:54 -07:00
gesslar
332e4b0307
correcting pcre_replace_callback documentation (#1201) 2026-06-23 21:05:01 -07:00
gesslar
c90b7d0879
document sort_array extra args forwarded to comparator (#1202)
The efun spec (mixed *sort_array(mixed *, int|string|function, ...))
and process_efun_callback both support trailing extra arguments that
are passed to the comparison function on each call, but the docs only
showed the three base forms. Update the synopsis to match map_array
and filter_array, and add a paragraph describing the behaviour.
2026-06-23 21:04:39 -07:00
gesslar
677b67ea42
Add PCRE flag support to PCRE efuns and docs/tests (#1166)
* adding support for optional named capture groups

* Add PCRE flag support and docs/tests
2025-12-25 13:26:17 -08:00
gesslar
a63ebaecf8
Add TLS server socket support with certificate and key options (#1146)
* fix/issue-1072-server-tls

* adding test

* couple of shoring up

* Initialize socket options array to prevent reference count leaks

When a socket is created, the options array was not being explicitly
initialized, relying on zero-initialization from the struct constructor.
However, this could lead to issues when clearing the socket later.

This fix explicitly initializes all socket options to const0u when the
socket is created, ensuring proper initialization and cleanup of option
values, particularly for TLS cert/key strings which would otherwise leak
references.

Fixes the "Bad ref count for shared string" errors when using
socket_set_option() with TLS certificate and key options.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix TLS socket option string reference counting

The socket_set_option() function was using assign_svalue_no_free() to store
string options (cert/key/hostname), which incremented the ref count but left
the argument string on the stack unreferenced. This caused ref count leaks
when the socket was closed.

Changed to use make_shared_string() like the callback functions do, creating
a proper copy of the string with correct ref counting. The old value is freed
first, and then a new shared string is created and stored.

This fixes the "Bad ref count for shared string" errors in the TLS server
socket tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Use assign_svalue() instead of manual string handling for TLS options

The previous approach using make_shared_string() was incrementing the ref
count an extra time because the string was already a shared string from
the compile-time constant. This caused ref=3 instead of ref=2.

Changed to use assign_svalue() which properly manages ref counts: it frees
the old destination value and copies the source with correct ref counting.
When pop_3_elems() is called, the stack element is properly dereferenced.

This ensures balanced ref counting for TLS certificate and key options.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* sockets: drop TLS option refs after use

* sockets: free ssl_ctx even when ssl missing

* sockets: clear blocked flag after TLS handshake

* sockets: fire write handler immediately after TLS handshake

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-04 12:33:51 -08:00
gesslar
3977897e2e
Add simplified syntax for function pointers and invocation (#1148)
* first-class-functions

* first-class-functions

* adding grammar.autogen.cc as requested. did not generate a .h after merging in last update

---------

Co-authored-by: Yucong Sun <1256464+thefallentree@users.noreply.github.com>
2025-11-12 23:53:35 -08:00
Yucong Sun
ddf8b9740d
more doc fixes. (#1137)
* docs: Fix multiple documentation issues

This commit addresses several documentation inconsistencies and gaps
identified by comparing docs with actual code implementations:

**Build Documentation Fixes:**
- Updated macOS build instructions to clarify Homebrew paths for both
  Apple Silicon and Intel Macs, emphasizing modern Homebrew defaults
- Added clarity to SQLite version options (=1 vs =2) explaining the
  differences and recommending version 2

**CLI Tool Documentation:**
- Added documentation for the `symbol` utility (docs/cli/symbol.md)
  - Tool for loading and analyzing LPC files
  - Usage: symbol <config> lpc_file
- Added documentation for the `portbind` utility (docs/cli/portbind.md)
  - Privilege-separated port binding for FluffOS
  - Allows binding to privileged ports then dropping privileges

**Apply Documentation:**
- Added mxp_enable apply (docs/apply/interactive/mxp_enable.md)
  - Called when MXP protocol is negotiated with client
- Added mxp_tag apply (docs/apply/interactive/mxp_tag.md)
  - Processes MXP tags from client
- Added zmp_command apply (docs/apply/interactive/zmp.md)
  - Handles ZMP (Zenith Mud Protocol) commands
- Added receive_ed apply (docs/apply/interactive/receive_ed.md)
  - Post-processes ed editor output
- Removed obsolete view_errors.md documentation
  - Apply not found in source code, appears to be deprecated

All new documentation follows existing format conventions and includes
cross-references to related efuns and applies where applicable.

* docs: Add missing apply and efun documentation

This commit adds documentation for previously undocumented applies and
efuns, completing the documentation coverage for recent FluffOS features.

**Apply Documentation:**
- terminal_colour_replace (interactive): Custom color token replacement
  callback for terminal_colour() efun preprocessing
- parser_error_message (master): Custom error message generation for
  parse_sentence() failures

**Efun Documentation:**
- hash() (crypto package): Complete documentation for cryptographic hash
  function with support for modern algorithms (SHA-3, BLAKE2, SM3)
  - Includes security recommendations and version compatibility notes
  - Documents all supported algorithms from legacy (MD5, SHA-1) to modern
    (SHA-3, BLAKE2b512)
  - Created new crypto package documentation directory

**Index Updates:**
- Updated docs/apply/interactive/index.md with new applies:
  mxp_enable, mxp_tag, receive_ed, terminal_colour_replace, zmp
- Updated docs/apply/master/index.md with parser_error_message
- Removed obsolete view_errors reference from master index
- Added crypto section to docs/efun/index.md

These additions address documentation gaps identified by comparing the
source code with existing documentation, particularly for the crypto
package enhancements from commit 33de35c (modern hash algorithms).

* docs: Add CLI tool, config guide, and documentation maintenance guide

This commit adds comprehensive documentation for additional FluffOS
features and creates a maintenance guide for the documentation itself.

**New CLI Documentation:**
- generate_keywords: Development tool for generating keywords.json for IDE
  integration and language server support. Extracts all efun metadata into
  structured JSON format.

**New Configuration Documentation:**
- config.md (driver/): Complete guide to driver configuration file format
  - Documents all configuration options with examples
  - Network setup (telnet, websocket, TLS)
  - Memory management and performance tuning
  - Protocol support (GMCP, MXP, ZMP, MSSP, MSP)
  - Security settings and limits
  - Includes practical examples for development and production

**Documentation Maintenance Guide:**
- CLAUDE.md (docs/): Comprehensive guide for maintaining FluffOS documentation
  - Documentation structure and organization
  - Templates for applies, efuns, and CLI tools
  - Workflow for finding and documenting undocumented features
  - Source code mapping (where to find implementations)
  - Verification and testing procedures
  - Common documentation issues and fixes
  - Package-specific notes and guidelines
  - Quick reference commands for contributors

**Index Updates:**
- Updated docs/driver/index.md to include config.md

These additions make it easier for contributors and AI assistants to
maintain accurate, complete documentation for FluffOS.

* docs: Add socket TLS options and LPC default arguments documentation

This commit documents important FluffOS features that were previously
undocumented, focusing on TLS socket options and LPC language enhancements.

**Socket TLS Options Documentation:**
- socket_set_option() efun - Configure socket TLS/SSL parameters
  - SO_TLS_VERIFY_PEER: Control peer certificate verification
  - SO_TLS_SNI_HOSTNAME: Set Server Name Indication hostname
  - Includes security notes and practical examples
  - Essential for HTTPS and secure socket connections

- socket_get_option() efun - Query socket option values
  - Retrieve TLS verification and SNI settings
  - Useful for validation and debugging
  - Examples for conditional logic and auditing

**LPC Language Feature:**
- Default Arguments documentation (prototypes.md)
  - Comprehensive guide to FluffOS default argument syntax
  - Feature added in commit bcb8e91 (2023) but not documented
  - Rules, examples, and use cases
  - Multiple practical examples showing API design patterns
  - Notes on compile-time behavior and limitations

**Index Updates:**
- Updated docs/efun/index.md with new socket functions
- Updated docs/efun/sockets/index.md with new functions

These features significantly enhance LPC programming capabilities:
1. TLS options enable secure network connections with certificate verification
2. Default arguments reduce code duplication and improve API usability

Both features are production-ready and widely used but were missing
from the documentation.

References:
- Socket options: commit 1fd7f61 (2023)
- Default arguments: commit bcb8e91 (2023)

* docs: Add comprehensive guides for tracing, TLS, and WebSocket support

Added three new concept documentation guides:

1. tracing.md - Complete guide to performance profiling with trace_start/trace_end
   - Chrome DevTools integration
   - Memory management warnings
   - Profiling scenarios (commands, combat, heartbeats)
   - Analysis techniques and best practices

2. tls.md - Comprehensive TLS/SSL support documentation
   - Server port TLS configuration
   - Certificate generation (self-signed and Let's Encrypt)
   - Client TLS socket connections with SO_TLS_VERIFY_PEER and SO_TLS_SNI_HOSTNAME
   - Security best practices
   - Troubleshooting guide

3. websocket.md - Complete WebSocket support guide
   - WebSocket server configuration (ws:// and wss://)
   - Multiple protocols (ASCII, Telnet, Binary, HTTP)
   - Built-in web client documentation and customization
   - GMCP over WebSocket
   - Telnet protocol over WebSocket
   - Security considerations and performance optimization
   - Troubleshooting and advanced usage

Updated concepts/general/index.md to include all three new guides.

* docs: Regenerate documentation indices

Ran update_index.sh to regenerate all documentation indices:

- docs/apply/index.md: Added new applies (mxp_enable, mxp_tag, receive_ed,
  terminal_colour_replace, zmp, parser_error_message), removed obsolete view_errors

- docs/concepts/index.md: Added new concept guides (tls, tracing, websocket)

- docs/driver/index.md: Added config documentation, updated title format

- docs/efun/crypto/index.md: Regenerated to standard format with hash function

- docs/cli/index.md: Generated index for CLI tools (driver, generate_keywords,
  json2o, lpcc, o2json, portbind, symbol)

All indices now correctly reference the new documentation added in previous commits.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-31 14:25:38 -07:00
M Lange
33de35ca19
feat: Add modern hash algorithms to crypto package (#1128)
* feat: Add modern hash algorithms to crypto package

Enhance the crypto package to support modern hash algorithms alongside
the existing legacy ones, using OpenSSL's EVP interface for new algorithms.

New algorithms added:
- SHA-3 family: sha3-224, sha3-256, sha3-384, sha3-512
- BLAKE2 family: blake2s256, blake2b512
- SM3 algorithm

Key features:
- Dual implementation approach: direct functions for legacy algorithms,
  EVP interface for modern ones
- Comprehensive OpenSSL version compatibility (1.0.x through 3.x)
- Graceful fallback behavior for unsupported algorithms
- Updated documentation in English and Chinese
- Enhanced test coverage

Compatibility notes:
- Legacy algorithms (MD4, MD5, SHA-1, SHA-2, RIPEMD160): All OpenSSL versions
- MD2, MDC2: OpenSSL 1.x-2.x only (removed in 3.x)
- BLAKE2 family: OpenSSL 1.1.0+
- SHA-3 family and SM3: OpenSSL 1.1.1+

Total supported algorithms: 17 (with version-dependent availability)

* Delete CRYPTO_PACKAGE_ENHANCEMENT.md
2025-07-27 19:55:53 -07:00
gesslar
ffdd2f55ec
Update and expand documentation for parsing functions (#1114)
* Add documentation for missing parsing functions and update existing entries

* ran `update_index.sh` and evidently some index.md files needed updating that hadn't been in the past. they were lonely and i thought, well, why not come along for the ride. you are welcome to join the rest of us in the parade of documentation updates.
2025-03-23 19:17:06 -04:00
gesslar
4368674a44
Update origin.md (#1111)
* Update origin.md

It now does not lie and say it returns an int, when it doesn't. It returns a string! After merge, will have @jlchmura update his references for the LPC Language server to stop me from getting angry messages from it telling me how an int and a string aren't meant to be together. Pssh.

* adding 'a'
2025-03-11 21:22:09 -07:00
William
11092fd24a
Websocket telnet fixes (#1100)
* Change on_user_websocket_received to use maybe_schedule_user_command

* Remove unneeded comments

* Fix msdp_send_variable string type

* Add MSDP docs and docs for has_msp

* MSDP docs fix some copy paste data i missed

* Fix doc indexes

* Fix send_msdp_variable copy paste issue

* Move msdp index entries to proper alpha sorting

* Move protocol url to correct doc file

* Add testsuite support for msdp

* Fix typo in msdp index link

* Fix index link for send_msdp_variable
Add see also line to msp_enable for has_msp

* Fix some copy paste errors
2024-10-08 12:50:21 -07:00
gesslar
4a6fafb554
Updating external_start() to use debug_levels rather than debug_message to log (#1084)
* moving debug_messages to debug macro and set/clear_debug_level. updated testsuite command to use it

* updating doc for clear_debug_level

* making the messaging consistent for debug messages
2024-07-17 23:20:34 -07:00
gesslar
735c8728df
new apply: on_destruct() (#1073)
* Notify object that it is scheduled for destruction

* adding documentation

* adding try/catch to allow destruction to continue even with mudlib errors

* updating and adding new supporting efuns

* adding documentation

* tests written for on_destruct

* fixy fixy?

* another try?

* moving test objects to /single. maye this was causing the compile check issues?

---------

Co-authored-by: Yucong Sun <sunyucong@gmail.com>
2024-07-16 08:45:12 -07:00
Michael Programs
fd4c2e865c
unique_array documentation update (#1065)
* cleanup and rewrite unique_array doc

* update various array efun docs SEE ALSO sections
2024-07-06 16:25:02 -07:00
Michael Programs
87e2059b11
update efun/apply/concept documentation related to message() (#1062) 2024-06-06 03:07:25 -07:00
Dino M. Gambone
21d773451a
Updated English documentation for fetch_variable, store_variable, and event. (#1064)
* Updated English documentation for `fetch_variable`, `store_variable`, and `event` to clarify scoping rules.

* Corrected header level for See Other
2024-06-06 03:06:35 -07:00
Michael Programs
0654ca48e3
update remove_interactive doc (#1069) 2024-06-06 03:05:39 -07:00
Michael Programs
a95022e246
cleanup efun::has_gmcp text (#1056) 2024-02-23 13:19:08 -08:00
Michael Programs
a6c895fbb0
update has_gmcp docs to mention enable gmcp in config (#1055) 2024-02-18 23:30:25 -08:00
gesslar
0e6c4d345b
query_num: Rewording the doc and adding workaround to handle negative numbers. (#1053)
* Rewording the doc and adding workaround to handle negative numbers.

* oops, fixing parens around function
2024-02-16 07:48:19 -08:00
Michael Programs
442339c850
update docs related to this_player (#1052)
* update efun docs related to this_player

* update efun doc indexes
2024-02-16 07:47:36 -08:00
Michael Programs
1ec4ec373c fix async_db_exec docs and test to use correct callback argument name 2024-01-14 01:08:34 -05:00
gesslar
654c45c895
adding doc for filter() (#1029) 2023-12-11 18:56:31 -08:00
gesslar
ec1da6f1eb
adding docs for has_mxp(), and has_zmp() (#1028) 2023-12-10 10:17:12 -08:00
gesslar
b93a6e6901
* adding clear_debug_level (#1026)
* updating set_debug_level
* updating debug_levels
2023-12-10 09:39:10 -08:00
gesslar
b2a5cf9dc3
Update get_config.md (#1022)
removing indentation for example so that the C code highlights.
2023-12-09 22:12:05 -08:00
gesslar
6d120973d4
Update set_config.md (#1023)
removing indent so that the C code properly highlights.
2023-12-09 22:11:39 -08:00
gesslar
6b8da5bab7
Adding documentation for debug_levels (#1025)
Updating documentation for set_debug_level
2023-12-09 22:11:05 -08:00