feat: REPL improvements - persistent variables, focus tracking, tab completion#371
Conversation
When running in REPL interactive mode, msg() output is now prefixed
with "msg: " to distinguish it from expression evaluation results.
This makes it clearer when output comes from msg() calls vs return
values.
Batch mode (scripts run with -e or file arguments) is unaffected -
msg() output appears without prefix as before.
Implementation:
- Added repl_is_active() function to check if REPL is running
- langmsgfunc() checks this flag before outputting
- Flag is set true when REPL starts, false when it exits
Example REPL session:
[root]> msg("Hello")
msg: Hello
true
[root]> "Hello"
Hello
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement variable persistence for the CLI REPL using a callback mechanism
that syncs variables from `with` block local tables to a persistent storage
location in the object database.
## Architecture
- Variables created in REPL expressions persist in system.temp.FrontierREPL.variables
- Each REPL evaluation is wrapped with `with system.temp.FrontierREPL.variables { ... }`
- A callback hook in langpoplocalchain() captures variables before local table disposal
- The callback syncs new/modified variables to the persistent table (skipping "with" refs)
## New Files
- frontier-cli/repl_variables.c/.h: REPL variable management subsystem
- repl_variables_init(): Creates system.temp.FrontierREPL structure
- repl_eval_with_variables(): Wraps scripts and evaluates with persistence
- Callback for syncing variables before local table disposal
## Modified Files
- Common/headers/langinternal.h: Add langmagictabledisposecallback type/global
- Common/source/lang.c: Define langmagictabledisposecallback global
- Common/source/langcallbacks.c: Call callback in langpoplocalchain()
- Common/source/langevaluate.c: Add logging.h include for trace macros
- frontier-cli/repl.c: Integrate repl_variables subsystem
- frontier-cli/Makefile: Add repl_variables.c to build
## Behavior
```
> x = 10
10
> y = x * 2
20
> x + y
30
```
Variables persist across REPL commands within a session. When running without
a system root, persistence is disabled and REPL falls back to non-persistent
evaluation with a warning.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extracted the path completion logic into a helper function `complete_slash_command_path()` and added support for tab-completing path arguments to both /jump and /list commands. Previously, only /jump supported tab completion for its path argument. Now both commands support: - Single-component paths (searching roottable and system.paths) - Dotted paths (navigating table hierarchy) - Trailing dot appended to table completions for continued navigation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tab completion for /jump and /list path arguments now considers the currently focused table first, before falling back to roottable and system.paths entries. Search order for single-component paths: 1. Current focused table (highest priority) 2. Root table entries 3. system.paths entries (lowest priority) This allows `/jump inetd` to find `user.inetd` when focused on the `user` table, while still falling back to system.paths resolution if not found locally. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add repl_set_focus() to update the focus address whenever the user navigates with /jump. The focus is stored as an address value pointing directly to the current table. Implementation uses setexemptaddressvalue() to avoid tmpstack interactions that could corrupt interpreter state during REPL command handling. Note: target.set() was removed from the script wrapper because calling target.set() multiple times crashes due to a pre-existing bug in the target verb implementation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In repl_sync_variables_callback(), after copyvaluerecord() creates a copy of a variable and hashassign() successfully stores it in the persistent table, the value must be removed from the tmpstack. Without this call, when the local table is disposed and the tmpstack is cleared, the value would be disposed again even though it is now owned by the hash table - causing a double-free crash. The bug manifested as crashes on any second REPL evaluation, with errors like pointer being freed was not allocated. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Function/script values (codevaluetype) require special handling to copy correctly. Attempting to copy them triggers assertion failures in langunpacktree due to tree packing complexities. For now, skip code values in the sync callback. This means functions defined in the REPL will not persist across evaluations (matching the behavior before persistent variables were added). TODO: Support function persistence in a future update. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
Code Review - PR #371: REPL ImprovementsOverviewThis PR adds significant REPL functionality including persistent variables, focus tracking, and enhanced tab completion. The implementation is generally solid with good architecture, but there are several areas requiring attention before merge. 🔴 Critical Issues1. Global Mutable State in Core Runtime (BLOCKING)Location: langmagictablecallback langmagictabledisposecallback = nil;Problem: This introduces global mutable state into the core language runtime. Per Impact:
Recommendation:
References:
2. Incorrect Include Path (BLOCKING)Location: #include "../../frontier-cli/repl.h" /* For repl_is_active() */Problem: Core runtime code (Common/) should NEVER include CLI-specific headers. This creates backwards dependency and violates layering. Impact:
Recommendation:
3. Missing tmpstack Protection (CRITICAL BUG RISK)Location: While you correctly added if (fl) {
exemptfromtmpstack(&valcopy); // ✅ Good
count++;
} else {
disposevaluerecord(valcopy, false); // ⚠️ Potential issue
log_warn(LOG_COMP_GENERAL, "Failed to assign variable %.*s",
(int)bsname[0], bsname + 1);
}Problem: If Recommendation: Add comment explaining tmpstack ownership in error path, or verify copyvaluerecord behavior.
|
|
Thanks for the thorough review! Re: Issue #1 (Global Mutable State) I agree this is architectural debt that should be addressed. However, converting the global callback to a thread-local or registration pattern is a larger refactoring effort that would expand the scope of this PR significantly. Could you please create a P0 issue to track this work? That way we can address it properly in a focused follow-up rather than rushing it as part of this PR. Re: Issue #2 (Incorrect Include Path) You're right - I'll fix this by moving the interactive mode check to a runtime flag that the CLI sets, removing the dependency on CLI headers from Common/. Re: Other Issues I'll address:
Will push fixes shortly. |
Blocking issues fixed: - Remove #include of frontier-cli/repl.h from Common/source/langverbs.c - Add flreplmode runtime flag instead of calling repl_is_active() - CLI sets flag at REPL start, clears at exit High priority issues fixed: - Add integration tests for focus tracking (4 tests) - Add integration tests for msg() prefix behavior (2 tests) - Add integration tests for variables persisting across errors (3 tests) - Mark function persistence tests as skipped with documentation - Add context guard documentation to repl_set_focus() - Improve logging levels (sync summary now log_info) - Add flow diagram comment to sync callback Test results: 1698 total, 1451 passed, 151 skipped, 96 failed (10 new tests added, 2 fewer failures from proper skip annotations) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review - PR #371: REPL improvementsThis PR introduces a well-designed persistent variables system for the REPL with focus tracking and improved tab completion. The implementation is clean and the architecture is sound. I have some recommendations for further improvements. ✅ Strengths
🔍 Issues & Recommendations1. CRITICAL: Global Mutable State ViolationLocation: extern boolean flreplmode; /* Global flag */Issue: This introduces global mutable state into the language runtime, which violates the architectural principle documented in
Why This Matters:
Recommended Fix: // Option 1: Add to existing TLS structure (if one exists)
// Option 2: Add to langcallbacks structure
// Option 3: Check if repl_is_active() directly from langverbs.c
// Preferred approach - call the REPL module directly:
extern boolean repl_is_active(void); // Already exists in repl.h
// In langverbs.c:
if (repl_is_active()) {
fputs("msg: ", stdout);
}This keeps the flag encapsulated in the REPL module where it belongs, rather than leaking into the core runtime. 2. MEDIUM: Missing Documentation of Callback ContractLocation: Issue: The callback typedef and comments are good, but the contract isn't fully specified. Recommendations:
3. MEDIUM: Tab Completion Code ComplexityLocation: Issue: The Recommendations:
4. LOW: Inconsistent Error HandlingLocation: Issue: Initialization of
Recommendation: Make error handling consistent and ensure proper cleanup in all paths. 5. LOW: Potential Memory Leak on Focus Assignment FailureLocation: Issue: If Question: Is 6. LOW: Magic Numbers in String LiteralsLocation: Issue: Pascal string length bytes are manually calculated: static byte namerepltable[] = "\x0c" "FrontierREPL"; /* 12 chars */Risk: If the string changes, the length byte could become incorrect. Recommendation: Use a macro or compile-time assertion: #define PSTRING(s) "\x" #s s
// Or use stringlength() assertions in init🧪 Testing Observations
📊 Performance ConsiderationsVariable Sync Performance: Optimization Opportunity (low priority):
Not critical for current use case, but worth noting for future optimization. 🔒 SecurityNo security concerns identified. The implementation:
📝 DocumentationExcellent:
Could Improve:
✅ VerdictThis is high-quality work with a solid architectural foundation. The main issue is the global Recommendation:
Once the global state issue is resolved, this PR will be ready to merge. 🎯 Suggested Next Steps
Great work overall! The persistent variables implementation is exactly what the REPL needed, and the code quality is very good. Just needs the architectural fix for global state. |
Response to Second ReviewThe second review recommends replacing However, this contradicts the first review's blocking issue #2:
The original code had
This is the correct architectural approach because:
The global state concern from the first review (issue #1 about Recommendation: Keep the current |
Summary
system.temp.FrontierREPL.variables/jumpnavigation is tracked insystem.temp.FrontierREPL.focus, accessible from UserTalk/listcommand now supports tab completion for path arguments/jumpand/listprioritizes the currently focused tablemsg()output in REPL mode for clarityChanges
Persistent Variables
system.temp.FrontierREPLtable structure with subtables forvariables,commands,target, andfocuslangpoplocalchainto sync variables from thewithblock's local table to persistent storageFocus Tracking
repl_set_focus()updatessystem.temp.FrontierREPL.focuswhenever/jumpnavigatesTab Completion
/listcommand argumentsBug Fixes
exemptfromtmpstack()call afterhashassign()to prevent double-freelangunpacktreeassertion failuresTest plan
Known Limitations
🤖 Generated with Claude Code