diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 687926fe..0ae00109 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -33,7 +33,12 @@ "Bash(git fetch --all --prune)", "Bash(git merge --no-ff:*)", "Bash(git add -A)", - "Bash(git commit -m \"wfl-ai: *\")" + "Bash(git commit -m \"wfl-ai: *\")", + "Bash(../target/release/wfl basic_syntax_comprehensive.wfl)", + "Bash(../target/release/wfl --parse basic_syntax_comprehensive.wfl)", + "Bash(target\\release\\wfl.exe:*)", + "Bash(targetreleasewfl.exe TestProgramstest_length.wfl)", + "Bash(../target/release/wfl.exe test_redefinition_error.wfl:*)" ], "deny": [] } diff --git a/.gitignore b/.gitignore index 94537342..1f034fac 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ combined/ *.log + +# Debug output files +*_debug.txt +wfl_exec.log diff --git a/CDR,md b/CDR,md deleted file mode 100644 index 58dd178e..00000000 --- a/CDR,md +++ /dev/null @@ -1,187 +0,0 @@ -Critical Design Review (CDR) Plan for WFL -Purpose -The purpose of this CDR is to evaluate the design of WFL comprehensively, ensuring it is free from critical issues that could compromise its functionality or stability. This static review analyzes the codebase without execution, targeting potential defects such as: - - Memory Leaks: Unreleased allocated memory that accumulates over time. - Infinite Loops: Loops that fail to terminate, potentially freezing the system. - Data Loss: Scenarios where data could be unintentionally lost or corrupted (a severe showstopper requiring immediate resolution). - Concurrency Issues: Deadlocks or borrow errors in asynchronous code. - Unsafe Code: Improper use of unsafe blocks or Foreign Function Interface (FFI). - -Review Process -The CDR follows a structured and consistent approach for every review: - - Pre-Step: Machine Checks: - Run cargo clippy --all -- -D warnings to catch common issues. - Run cargo udeps to identify unused dependencies. - Run cargo geiger to detect unsafe code usage. - Static Review: - At least two passes are conducted per CDR, analyzing source code (src.txt) and documentation (docs.txt) without runtime execution. - Additional passes are performed if Critical or Major issues remain after the second pass. - Each pass is time-boxed: 90 minutes for AI analysis + 30 minutes for human triage. - Consistency: The same review routine (detailed below) is applied to every CDR for uniformity and thoroughness. - Artifacts: Each pass produces a markdown file (e.g., cdr_pass1.md) stored in docs/cdr/, summarizing findings, status, and next steps. - CI Integration: Continuous Integration (CI) verifies CDR artifacts exist and checks for unresolved Critical/Major issues (e.g., grep for [ ] Critical checkboxes). - -Reviewer Roles & Sign-Off Gates - - Primary Reviewer: AI (performs initial analysis). - Human Lead: Senior developer or architect (reviews findings, triages issues). - Maintainer: Final approver (ensures all Critical issues are resolved). - Sign-Off Criteria: - All Critical issues resolved (Red = 0). - Major issues ≤ 2 (Yellow ≤ 2). - Project does not advance phases until all Critical issues are closed. - -Key Focus Areas -The review prioritizes these critical aspects of the WFL design: - - Memory Leaks - Identify unreleased allocated memory (e.g., in Rc> structures). - Check for cyclic references. - Infinite Loops - Detect loops without proper termination conditions. - Assess recursive functions for infinite recursion risks. - Data Loss - Ensure no operations overwrite or discard data unintentionally. - Verify robust error handling to prevent corruption. - Concurrency Issues - Check for deadlocks or borrow errors in async code (e.g., RefCell misuse). - Unsafe Code - Ensure unsafe blocks or FFI usage are justified with safety comments. - Efficiency - Evaluate algorithms for performance bottlenecks (e.g., O(n²) complexity). - Check scalability with large inputs (e.g., 10,000 tokens). - Error Handling - Confirm comprehensive error handling to prevent crashes. - Validate recovery or graceful failure in error scenarios. - Security - Identify vulnerabilities (e.g., unsanitized inputs). - Ensure file paths are canonicalized to prevent path traversal. - Log Management - Prevent uncontrolled log growth or unconditional file operations. - -Grading System -Issues are categorized using this grading system: - - Critical (Red) - Description: Issues causing system failure, data loss, or security breaches. - Action: Must be addressed immediately. If unresolved after the third pass, new feature work freezes, and a hot-fix sprint is scheduled. - Examples: Data loss, infinite loops with no exit, memory leaks in core components. - Major (Yellow) - Description: Issues significantly impacting performance or functionality without halting the system. - Action: Should be resolved before final approval; temporary workarounds may be acceptable short-term. - Examples: Excessive memory usage, inefficient loop design, missing error handling for non-critical cases. - Minor (Green) - Description: Cosmetic or minimal-impact issues. - Action: Can be addressed in future iterations. - Examples: Unnecessary variable declarations, inconsistent formatting. - Informational (Blue) - Description: Optional improvement suggestions. - Action: Enhancements for future consideration. - Examples: Potential optimizations, alternative design choices. - -Review Checklist -This checklist guides the static review process: -Memory Management - - Are all allocated resources (e.g., Rc>, file handles) explicitly released? - Are there cyclic references in data structures (e.g., Value::List, Value::Object)? - Do recursive functions manage stack growth properly? - -Loop Constructs - - Are termination conditions for loops (while, for, forever) clearly defined and achievable? - Can external inputs or edge cases cause infinite loops? - Are recursive calls guaranteed to terminate? - Are timeouts configured (e.g., in REPL) to prevent indefinite hangs? - -Data Handling - - Are data structures manipulated to preserve integrity? - Do file I/O operations safeguard against data loss? - Are assignments/updates free from unintended overwrites? - Are file paths canonicalized to prevent vulnerabilities? - -Algorithm Efficiency - - Are algorithms optimized for large inputs (e.g., avoid O(n²) with n = 10,000 tokens)? - Are there bottlenecks (e.g., nested loops, excessive cloning)? - -Error Handling - - Are all error conditions caught and handled? - Is there a mechanism to recover from failures without data loss? - -Security - - Are user inputs sanitized to prevent injection or overflow? - Do external interactions (e.g., file I/O, network calls) include validation? - -Concurrency - - Are async operations free from deadlocks or RefCell borrow errors? - Are awaits placed to avoid holding RefCell borrows across suspension points? - -Unsafe Code - - Are all unsafe blocks or FFI usage justified with safety comments? - Are unsafe contracts enforced? - -Log Management - - Are log files managed to prevent uncontrolled growth? - Are file operations (e.g., File::create) guarded against errors? - -Condensed Checklist (Tear-Off Version) - -[ ] Rc / Weak cycles? -[ ] File handles closed? -[ ] while / for termination? -[ ] Recursion depth bounded? -[ ] I/O overwrites guarded? -[ ] Error paths = no data loss? -[ ] Clones / allocs in hot loops? -[ ] All Result/Option handled? -[ ] User input sanitized? -[ ] Async awaits inside RefCell? -[ ] Unsafe blocks justified? -[ ] Log files managed? - -Review Procedure -The CDR follows this step-by-step process: - - Pre-Step: Machine Checks - Run cargo clippy --all -- -D warnings, cargo udeps, and cargo geiger. - Address issues caught by these tools before proceeding. - Initial Review (Pass 1) - Conduct static analysis using the checklist. - Document findings in cdr_pass1.md, assigning grades (Red, Yellow, Green, Blue). - Focus on Critical issues (e.g., data loss, infinite loops). - Second Pass - After addressing Pass 1 issues, verify fixes in a second review. - Reassess against the checklist for consistency. - Escalate to additional reviews if Critical or Major issues persist. - Additional Reviews (if needed) - Conduct further passes until all Critical issues are resolved and Major issues ≤ 2. - Final Assessment - Compile a final report once Critical issues are resolved and Major issues ≤ 2. - Approve progression, noting Minor or Informational items for future work. - Feedback Loop - Require new unit/integration tests or linter rules for each Critical or Major issue to prevent recurrence. - -Example Application to WFL Codebase - - Memory Leaks (e.g., debug_report.rs) - Check SafeDebug for Rc> handling in Value::List and Value::Object. - Grade: Major (Yellow) for potential leaks without immediate impact; Critical (Red) if systemic. - Infinite Loops (e.g., repl.rs) - Assess run_repl loop for termination (CTRL-C or .exit). Ensure no input traps it indefinitely. - Grade: Critical (Red) if an infinite loop is possible. - Data Loss (e.g., interpreter/environment.rs) - Examine Environment updates in define and assign. Confirm no unintended overwrites. - Grade: Critical (Red) if data loss is detected. - -Conclusion -This CDR plan provides a systematic, repeatable evaluation of WFL’s design, prioritizing the detection and resolution of critical flaws like memory leaks, infinite loops, and data loss. With machine checks, defined reviewer roles, CI integration, and a feedback loop, it ensures WFL is robust and reliable for its intended applications. -This is the full updated CDR plan, ready for implementation. Let me know if you need additional assistance, such as a templated markdown version or CI integration scripts! \ No newline at end of file diff --git a/CDR/CDR3.md b/CDR/CDR3.md deleted file mode 100644 index fa44a59e..00000000 --- a/CDR/CDR3.md +++ /dev/null @@ -1,71 +0,0 @@ -Thanks! I’ll begin the third-pass Critical Design Review of the WFL project using the latest `src.md` and referencing both previous review passes. I’ll focus on verifying that newly added or modified elements haven’t introduced regressions or new critical/major issues. - -I’ll share the full `cdr_pass3.md` summary shortly, formatted to drop into your `docs/cdr/` directory. - -# WFL Project – Third-Pass CDR Results - -## Summary of Status -In this third-pass review, the WFL codebase shows continued improvement and new feature implementation. The previously identified critical issues in file I/O and unimplemented features have been **addressed** – file operations now preserve data and execute properly, and the HTTP and `try/when` statements are implemented as intended. The addition of a pattern-matching feature (`matches_pattern`, `find_pattern`, etc.) further extends WFL’s capabilities. Most fixes from the second pass remain effective; however, one prior issue – the **environment reference cycle** in function closures – was found to be **not fully resolved in the code**, representing a regression that can cause a memory leak. No other new critical problems were identified. A few **minor issues** persist (e.g. a misleading debug report message and a trivial REPL flush unwrap), and WFL remains single-threaded (concurrency syntax exists but executes sequentially). Overall, WFL is **stable and functional** with its current feature set, with the exception of the reopened memory leak. Addressing that leak and polishing remaining nits will bring the project very close to production-ready. - -## Issues by Severity - -### Critical Issues - -- **(Reopened) Memory Leak – Environment RC Cycle**: The fix to break the reference cycle between an `Environment` and function closures was not applied in the code, so the cycle (and potential leak) persists. When an action (function) is defined, it still captures the defining environment with a strong `Rc` and is stored in that same environment, creating a self-referential cycle ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20function%20%3D%20FunctionValue%20,column%2C)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20function_value%20%3D%20Value%3A%3AFunction%28Rc%3A%3Anew%28function%29%29%3B%20env,clone)). Rust’s reference counting will never free these, so any function definitions (especially in a REPL or long-running process) will leak memory. This is a **critical regression** because over time it can lead to unbounded memory growth. **Fix:** Implement the intended solution of using a `Weak>` for the captured environment (so the function doesn’t keep it alive). For example, make `FunctionValue.env` a `Weak` rather than `Rc` – then upgrade it on function call. This will break the cycle (allowing environments to drop) at the cost of making functions invalid if their defining scope goes away. If that trade-off is unacceptable for certain global or returned functions, an alternate design (such as cloning needed context or using a different memory management strategy) may be needed. At minimum, the cycle should be removed to uphold memory safety expectations ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=pub%20struct%20FunctionValue%20,usize%2C%20pub%20column%3A%20usize%2C)). - -### Major Issues - -- **None new identified.** All major issues noted in the previous pass have been resolved or downgraded. The HTTP (`open url ...`) and exception handling (`try/when`) features that were previously non-functional are now implemented (so they are no longer considered major gaps). The environment cycle issue discussed above is classified as critical due to its impact. No other major design flaws have been introduced. (The concurrency model remains essentially single-threaded by design – see Minor/Informational notes – but this was an existing design choice rather than a new issue.) - -### Minor Issues - -- **Debug Report Message Not Conditional:** When a debug report fails to write to file (due to an I/O error), the user is still told “Debug report created” unconditionally. The `create_report` function now handles errors by logging them instead of panicking ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20mut%20file%20%3D%20match,)), which is good, but the UI message is misleading if the report wasn’t actually saved. This could confuse users (they might look for a report that isn’t there). *Suggestion:* Indicate to the user when report generation fails – e.g. print a warning that the debug report could not be created. This ensures the user isn’t misled by a success message. - -- **REPL `.clear` Flush Unwrap:** The REPL’s `.clear` command clears the screen by writing an ANSI code and then flushing stdout with `.unwrap()` ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=,CommandResult%3A%3AClearedScreen)). In normal use this is harmless (stdout rarely fails), but in theory it could panic if the output stream is closed. This is a very minor issue. It could be made more robust by checking the flush result or using `expect()` with a message, but the impact is negligible. It’s acceptable to leave as-is, though handling the error (or ignoring it gracefully) would eliminate the theoretical panic. - -- **File Open Behavior (Create vs Open Existing):** The `open file` command now correctly does **not** truncate existing files (fixing the prior data loss) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=match%20tokio%3A%3Afs%3A%3AOpenOptions%3A%3Anew%28%29%20,)). It will also create the file if it doesn’t exist (since `.create(true)` is still used). This behavior is acceptable, but users might expect an error when attempting to read a non-existent file rather than silently creating an empty file. In addition, opening the same file twice now returns an error (“File already open”) to prevent handle conflicts ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=for%20,id%29%29%3B%20%7D)). This is a good resolution of the previous ambiguity. To further improve usability, consider documenting that `open file` will create a new file if it doesn’t exist (so users are aware that a typo in the filename could create an unexpected empty file). No code change is strictly needed here; it’s more about setting expectations in documentation or future enhancements (such as a distinct `create file` command). - -- **Minor Equality/Debug Quirks:** The implementation of list membership (`contains` and `indexof`) compares values by formatting them (`"{:?}"`) and checking string equality ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=for%20value%20in%20list.borrow%28%29.iter%28%29%20,)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20list%20%3D%20expect_list%28%26args,1)). This works for now but is not very efficient for large or complex values, and it might consider different values equal if their debug printouts coincide. This is a minor concern – typical use (numbers, texts, etc.) is fine. In the future, defining a proper equality check for `Value` (e.g., implement `PartialEq` for Value to compare by variant and content) would be more robust. It’s not urgent, but something to keep in mind as the language grows (especially if users store large objects in lists and frequently check membership). - -### Informational / Other Observations - -- **Concurrency is Sequential:** The `wait for ... and ...` syntax and `Future` type still do not run things in parallel. The interpreter executes `WaitForStatement` by simply running the inner statement immediately ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=Statement%3A%3AWaitForStatement%20,self.execute_statement%28inner%2C%20Rc%3A%3Aclone%28%26env%29%29.await)). There is no true concurrent task scheduling in WFL at this time, which is consistent with the project’s single-threaded design. This isn’t a bug per se (the code functions correctly, just synchronously), but users might not get the concurrency they expect from the syntax. It would be wise to clarify in documentation that actual parallel execution isn’t supported yet. If concurrency is a future goal, this area remains flagged for design work (task management, thread safety, etc.). For now, the current behavior is acceptable given that WFL is intended to run tasks one at a time. - -- **Cyclic Data Structures:** WFL allows users to create cyclic data (e.g., a list that contains itself) since values are reference-counted. The `SafeDebug` facility will detect cycles to avoid infinite prints, and tests confirm this works ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20list%20%3D%20Rc%3A%3Anew%28RefCell%3A%3Anew%28Vec%3A%3A,list)). However, such cycles will permanently consume memory because Rust’s `Rc` has no cycle garbage collection. This is an inherent limitation of using `Rc`. It’s not likely to affect typical scripts, so it’s noted only for awareness. In long-running sessions, users should avoid creating self-referential data structures indefinitely. This is more of a documentation note – no action needed unless memory usage from user-created cycles becomes a real problem in practice. - -- **Single-Threaded by Design:** As noted previously, the interpreter and data structures use `Rc>` pervasively and are **not `Send` or `Sync`**. WFL cannot be easily shared across threads. This is expected for a REPL/CLI tool and is not an issue unless future plans involve running WFL in a multi-threaded context. If that happens, significant refactoring (to use `Arc` and ensure thread-safe types) would be required. For now, the single-threaded approach keeps things simple. It may be worth mentioning in the README that WFL scripts shouldn’t be used from multiple threads simultaneously. - -- **Performance:** No new performance issues were observed. The pattern matching feature compiles a regex under the hood, but this is done on each call to `matches_pattern/find_pattern/etc.` rather than caching. For reasonable pattern usage this overhead is minor. All other operations (interpreting AST, I/O, etc.) remain on par with expectations for a scripting language. If performance hotspots emerge (e.g., heavy use of large regex patterns or very large data sets in pure WFL), profiling might be needed. As of now, the balance between clarity and performance is satisfactory. The earlier suggestions (like avoiding unnecessary cloning of large values) are still optional improvements but not required at this stage. - -- **Logging Behavior:** Logging initialization is now robust – it falls back to console logging if the log file can’t be created ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20file_logger_result%20%3D%20File%3A%3Acreate%28file_path%29.map%28%7Cfile%7C%20,clone%28%29%2C%20file%2C%20%29)). Each run truncates the log file on start (so logs don’t grow unbounded across runs). In a long REPL session, logs will accumulate until the session ends, which is expected. There is no log rotation or size limit, but this is fine for the current use-case. The log includes timestamps and retains console output at Info level or higher. No further issues in logging; just keep in mind that if WFL were to be used in a persistent service, a more advanced logging setup might be desired (not urgent now). - -- **Test Coverage:** The test suite has grown to cover the new features and fixes. There are tests for pattern parsing and matching (including named capture extraction) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=if%20let%20Value%3A%3AObject,borrow)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=Value%3A%3AText%28Rc%3A%3Afrom%28)), for proper file I/O behavior (write-then-read, double open errors), for the REPL `.clear` command ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=%5Btokio%3A%3Atest%5D%20async%20fn%20test_clear_command%28%29%20)), and for preventing the environment leak (the second pass tests assumed the leak was fixed) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=match%20at%20L5744%20assert%21%28func)). These tests help ensure that resolved issues stay resolved. It’s recommended to add a test for the environment cycle fix once implemented (e.g., ensure that dropping an interpreter frees all environments or that defining a function in a nested scope doesn’t leak). Overall, test coverage is solid and contributes to confidence in the code’s stability. - -## Review Checklist - -- [ ] **Memory Leaks / RC Cycles:** **Not yet resolved.** The cycle between functions and their defining environment is still present, causing a memory leak ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20function%20%3D%20FunctionValue%20,column%2C)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20function_value%20%3D%20Value%3A%3AFunction%28Rc%3A%3Anew%28function%29%29%3B%20env,clone)). This is a regression from the expected fix. Breaking this cycle via `Weak` references is a priority to reclaim memory from defunct scopes. No other new RC cycles were found in new features (pattern matching creates values but doesn’t introduce lasting cycles). -- [x] **Infinite Loops / Recursion Safety:** **Completed.** Timeouts are applied uniformly to scripts and REPL. The REPL now uses the default 60s execution limit (via `Interpreter::with_timeout` on startup ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=impl%20ReplState%20,timeout_seconds))), so an infinite loop will time out in both script and interactive modes. Deep recursion is likewise bounded by the timeout (and in debug builds an assertion prevents excessive call stack growth). No unbounded recursion or hang was observed in this pass. -- [x] **Data Loss (File I/O):** **Resolved.** File content is no longer erased on open – `IoClient::open_file` uses `.truncate(false)` ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=match%20tokio%3A%3Afs%3A%3AOpenOptions%3A%3Anew%28%29%20,)). The interpreter properly implements `write_file` and `close_file` by calling the `IoClient` and propagating errors ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=%7D%20%3D,await)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=match%20self.io_client.write_file%28%26file_str%2C%20%26content_str%29.await%20)). We verified that writing to a file then reading returns the expected content, and closing a file releases the handle. The prior issues (silent no-ops and data truncation) are fixed. Variables and data in the interpreter are preserved as expected; no runtime data loss issues were found. -- [ ] **Concurrency & Borrowing:** **Flagged (unchanged).** WFL is still single-threaded and sequential. No inherent data races occur under the current design (all code runs on one thread and `RefCell` enforces borrow rules). The file handle management, previously a potential race, has been made race-safe by holding locks during handle cloning and removal ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20mut%20file_handles%20%3D%20self)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20mut%20file_handles%20%3D%20self)). If true concurrency is introduced in the future, additional safeguards will be needed (e.g., making the interpreter `Send`, protecting shared state with mutexes, etc.). For now, this item remains noted for future consideration – there’s no concurrency in effect, so no immediate issue beyond the unimplemented parallelism noted above. -- [x] **Unsafe Code / FFI:** **Completed.** The codebase remains free of `unsafe` code. New features (like HTTP via `reqwest` and regex via `regex` crate) are used through safe APIs. All external calls are handled through well-vetted libraries. No FFI or undefined behavior concerns were found. -- [x] **Algorithmic Efficiency:** **Acceptable.** No new algorithms with problematic complexity were added. Pattern matching uses Rust’s regex engine, which is efficient for the given pattern grammar. The interpreter is still a tree-walk execution – adequate for the intended script sizes. Minor inefficiencies (like stringifying values to compare them) are present but not critical. There is currently no evidence of performance bottlenecks in normal use. -- [x] **Error Handling Robustness:** **Mostly completed.** The vast majority of `.unwrap()` calls have been removed or are only present in test code. All I/O operations return `Result` with error messages rather than panicking. The logging initializer handles errors gracefully ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20file_logger_result%20%3D%20File%3A%3Acreate%28file_path%29.map%28%7Cfile%7C%20,clone%28%29%2C%20file%2C%20%29)). One minor place for improvement is the `.clear` command flush (uses `unwrap`), but this is very low impact. Also, as noted, the debug report creation logs errors instead of panicking, which is good – the remaining tweak is to inform the user on failure. Overall, runtime errors are properly caught and turned into `RuntimeError` results; the interpreter doesn’t crash on script errors or bad inputs. -- [x] **Security (Input/Path Handling):** **Completed.** There are no known security vulnerabilities in how WFL handles input or file paths. File paths are used directly as given; with the new behavior, opening a file won’t destroy its contents and will error if already open, reducing unintended side effects. All network access via `http_get`/`http_post` is explicit in scripts – there’s no arbitrary code execution or injection risk within the language itself. (Of course, scripts can perform destructive actions on the host system if run, but that is expected in a language that interfaces with file I/O and the network – running untrusted WFL scripts is not advised unless they are sandboxed externally.) If WFL is ever embedded in a larger application to run untrusted code, additional sandboxing would be needed, but that is beyond the current scope. -- [x] **Logging and Debugging:** **Completed.** Logging no longer panics on setup, and important events are logged with timestamps. The debug report system provides a detailed dump on errors. The only nit is the user message on report failure, as discussed. Logs are flushed on program exit by design; in long runs, data is buffered but can be manually flushed if needed. The debug report includes call stack and local variables, which greatly aids troubleshooting. No logging-related errors were observed during this pass. - -## Suggested Next Steps - -1. **Fix the Function Closure Memory Leak:** Implement the solution to break the `Environment -> Function -> Environment` cycle. The straightforward fix is to change the function’s captured `env` to a `Weak>` and adjust function calls to upgrade that weak reference. This will prevent memory from leaking when environments go out of scope. After implementing, add tests to ensure that defining a function inside another (or in a loop) doesn’t increase memory usage after those scopes end. This is the most crucial fix to make before any long-running usage of WFL. - -2. **Clarify or Enhance Concurrency Features:** Since true parallel execution isn’t implemented, consider updating the documentation or help text to reflect that **`wait for … and …` is sequential** for now. If concurrency is on the roadmap, begin designing how futures would run (e.g., using `tokio::join!` or spawning tasks). This includes making the interpreter state thread-safe or otherwise partitioning state per task. If concurrency is not a near-term goal, it might even be wise to disable the syntax or make `wait for` simply execute both sub-statements back-to-back (to avoid implying parallelism). This will manage user expectations and allow you to introduce real concurrency when ready, without the current “pseudo-concurrency” confusion. - -3. **Document New Features and Behaviors:** Update the README or user guide to cover the newly implemented features (HTTP operations, try/when, pattern matching). Note the behaviors such as `open file` creating files if they don’t exist, the lack of real parallelism, and what the pattern syntax is (e.g., explain that `"3 digits"` or `"{name}"` can be used and that `find_pattern` returns an object of captures). Clear documentation will prevent misuse and bug reports that are really misunderstandings. For example, explicitly stating that WFL is currently single-threaded and that `wait for` is not yet parallel will save users confusion. - -4. **Minor Robustness Polishing:** Tackle the small remaining polish items. For instance, change the debug report message to indicate failure if `create_report` couldn’t write the file (perhaps have `create_report` return a `Result` instead of always `PathBuf`). This is a small change that improves UX. Similarly, you could replace the `.unwrap()` in `.clear` with proper error handling or simply ignore a flush error. These tweaks are low-effort and will make the tool feel more mature and reliable, even in edge cases. - -5. **Improve `Value` Equality (Future Idea):** If the project grows, implementing proper equality for values might be useful (so that `==` in the language or `contains` in a list works intuitively for all types). This could involve adding an `Eq` trait implementation for `Value` or a custom comparison method that handles numbers, text, lists, objects, etc. by content. It’s not urgent for current functionality, but as a forward-looking enhancement it would avoid reliance on string debug representations for comparisons. This goes hand-in-hand with possibly implementing the `Display` trait for `Value` (to differentiate user-facing printouts from `Debug` which is for developers). - -6. **Continue Testing and Monitoring:** After fixing the closure leak and any tweaks, perform another round of targeted testing. In particular, test memory usage by defining many functions in a loop or REPL to confirm no leaks. Also test scenarios with the pattern matching to ensure no panics or unexpected behavior (e.g., malformed patterns should return errors gracefully). Keep an eye on issue reports or user feedback for any performance problems or edge-case errors that weren’t anticipated. Expanding the test suite alongside new features (as you’ve been doing) will maintain confidence. - -By addressing the above, WFL will eliminate its last critical flaw and refine the user experience. The project has come a long way: it now reliably handles file I/O, networking, and error handling in a safe manner. With the memory leak fixed and documentation updated, the focus can shift to new features or performance improvements with a solid foundation in place. Great work so far – resolving this remaining issue will put the project in excellent shape for a 1.0 release. - diff --git a/CDR/CDR3.txt b/CDR/CDR3.txt deleted file mode 100644 index fa44a59e..00000000 --- a/CDR/CDR3.txt +++ /dev/null @@ -1,71 +0,0 @@ -Thanks! I’ll begin the third-pass Critical Design Review of the WFL project using the latest `src.md` and referencing both previous review passes. I’ll focus on verifying that newly added or modified elements haven’t introduced regressions or new critical/major issues. - -I’ll share the full `cdr_pass3.md` summary shortly, formatted to drop into your `docs/cdr/` directory. - -# WFL Project – Third-Pass CDR Results - -## Summary of Status -In this third-pass review, the WFL codebase shows continued improvement and new feature implementation. The previously identified critical issues in file I/O and unimplemented features have been **addressed** – file operations now preserve data and execute properly, and the HTTP and `try/when` statements are implemented as intended. The addition of a pattern-matching feature (`matches_pattern`, `find_pattern`, etc.) further extends WFL’s capabilities. Most fixes from the second pass remain effective; however, one prior issue – the **environment reference cycle** in function closures – was found to be **not fully resolved in the code**, representing a regression that can cause a memory leak. No other new critical problems were identified. A few **minor issues** persist (e.g. a misleading debug report message and a trivial REPL flush unwrap), and WFL remains single-threaded (concurrency syntax exists but executes sequentially). Overall, WFL is **stable and functional** with its current feature set, with the exception of the reopened memory leak. Addressing that leak and polishing remaining nits will bring the project very close to production-ready. - -## Issues by Severity - -### Critical Issues - -- **(Reopened) Memory Leak – Environment RC Cycle**: The fix to break the reference cycle between an `Environment` and function closures was not applied in the code, so the cycle (and potential leak) persists. When an action (function) is defined, it still captures the defining environment with a strong `Rc` and is stored in that same environment, creating a self-referential cycle ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20function%20%3D%20FunctionValue%20,column%2C)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20function_value%20%3D%20Value%3A%3AFunction%28Rc%3A%3Anew%28function%29%29%3B%20env,clone)). Rust’s reference counting will never free these, so any function definitions (especially in a REPL or long-running process) will leak memory. This is a **critical regression** because over time it can lead to unbounded memory growth. **Fix:** Implement the intended solution of using a `Weak>` for the captured environment (so the function doesn’t keep it alive). For example, make `FunctionValue.env` a `Weak` rather than `Rc` – then upgrade it on function call. This will break the cycle (allowing environments to drop) at the cost of making functions invalid if their defining scope goes away. If that trade-off is unacceptable for certain global or returned functions, an alternate design (such as cloning needed context or using a different memory management strategy) may be needed. At minimum, the cycle should be removed to uphold memory safety expectations ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=pub%20struct%20FunctionValue%20,usize%2C%20pub%20column%3A%20usize%2C)). - -### Major Issues - -- **None new identified.** All major issues noted in the previous pass have been resolved or downgraded. The HTTP (`open url ...`) and exception handling (`try/when`) features that were previously non-functional are now implemented (so they are no longer considered major gaps). The environment cycle issue discussed above is classified as critical due to its impact. No other major design flaws have been introduced. (The concurrency model remains essentially single-threaded by design – see Minor/Informational notes – but this was an existing design choice rather than a new issue.) - -### Minor Issues - -- **Debug Report Message Not Conditional:** When a debug report fails to write to file (due to an I/O error), the user is still told “Debug report created” unconditionally. The `create_report` function now handles errors by logging them instead of panicking ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20mut%20file%20%3D%20match,)), which is good, but the UI message is misleading if the report wasn’t actually saved. This could confuse users (they might look for a report that isn’t there). *Suggestion:* Indicate to the user when report generation fails – e.g. print a warning that the debug report could not be created. This ensures the user isn’t misled by a success message. - -- **REPL `.clear` Flush Unwrap:** The REPL’s `.clear` command clears the screen by writing an ANSI code and then flushing stdout with `.unwrap()` ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=,CommandResult%3A%3AClearedScreen)). In normal use this is harmless (stdout rarely fails), but in theory it could panic if the output stream is closed. This is a very minor issue. It could be made more robust by checking the flush result or using `expect()` with a message, but the impact is negligible. It’s acceptable to leave as-is, though handling the error (or ignoring it gracefully) would eliminate the theoretical panic. - -- **File Open Behavior (Create vs Open Existing):** The `open file` command now correctly does **not** truncate existing files (fixing the prior data loss) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=match%20tokio%3A%3Afs%3A%3AOpenOptions%3A%3Anew%28%29%20,)). It will also create the file if it doesn’t exist (since `.create(true)` is still used). This behavior is acceptable, but users might expect an error when attempting to read a non-existent file rather than silently creating an empty file. In addition, opening the same file twice now returns an error (“File already open”) to prevent handle conflicts ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=for%20,id%29%29%3B%20%7D)). This is a good resolution of the previous ambiguity. To further improve usability, consider documenting that `open file` will create a new file if it doesn’t exist (so users are aware that a typo in the filename could create an unexpected empty file). No code change is strictly needed here; it’s more about setting expectations in documentation or future enhancements (such as a distinct `create file` command). - -- **Minor Equality/Debug Quirks:** The implementation of list membership (`contains` and `indexof`) compares values by formatting them (`"{:?}"`) and checking string equality ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=for%20value%20in%20list.borrow%28%29.iter%28%29%20,)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20list%20%3D%20expect_list%28%26args,1)). This works for now but is not very efficient for large or complex values, and it might consider different values equal if their debug printouts coincide. This is a minor concern – typical use (numbers, texts, etc.) is fine. In the future, defining a proper equality check for `Value` (e.g., implement `PartialEq` for Value to compare by variant and content) would be more robust. It’s not urgent, but something to keep in mind as the language grows (especially if users store large objects in lists and frequently check membership). - -### Informational / Other Observations - -- **Concurrency is Sequential:** The `wait for ... and ...` syntax and `Future` type still do not run things in parallel. The interpreter executes `WaitForStatement` by simply running the inner statement immediately ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=Statement%3A%3AWaitForStatement%20,self.execute_statement%28inner%2C%20Rc%3A%3Aclone%28%26env%29%29.await)). There is no true concurrent task scheduling in WFL at this time, which is consistent with the project’s single-threaded design. This isn’t a bug per se (the code functions correctly, just synchronously), but users might not get the concurrency they expect from the syntax. It would be wise to clarify in documentation that actual parallel execution isn’t supported yet. If concurrency is a future goal, this area remains flagged for design work (task management, thread safety, etc.). For now, the current behavior is acceptable given that WFL is intended to run tasks one at a time. - -- **Cyclic Data Structures:** WFL allows users to create cyclic data (e.g., a list that contains itself) since values are reference-counted. The `SafeDebug` facility will detect cycles to avoid infinite prints, and tests confirm this works ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20list%20%3D%20Rc%3A%3Anew%28RefCell%3A%3Anew%28Vec%3A%3A,list)). However, such cycles will permanently consume memory because Rust’s `Rc` has no cycle garbage collection. This is an inherent limitation of using `Rc`. It’s not likely to affect typical scripts, so it’s noted only for awareness. In long-running sessions, users should avoid creating self-referential data structures indefinitely. This is more of a documentation note – no action needed unless memory usage from user-created cycles becomes a real problem in practice. - -- **Single-Threaded by Design:** As noted previously, the interpreter and data structures use `Rc>` pervasively and are **not `Send` or `Sync`**. WFL cannot be easily shared across threads. This is expected for a REPL/CLI tool and is not an issue unless future plans involve running WFL in a multi-threaded context. If that happens, significant refactoring (to use `Arc` and ensure thread-safe types) would be required. For now, the single-threaded approach keeps things simple. It may be worth mentioning in the README that WFL scripts shouldn’t be used from multiple threads simultaneously. - -- **Performance:** No new performance issues were observed. The pattern matching feature compiles a regex under the hood, but this is done on each call to `matches_pattern/find_pattern/etc.` rather than caching. For reasonable pattern usage this overhead is minor. All other operations (interpreting AST, I/O, etc.) remain on par with expectations for a scripting language. If performance hotspots emerge (e.g., heavy use of large regex patterns or very large data sets in pure WFL), profiling might be needed. As of now, the balance between clarity and performance is satisfactory. The earlier suggestions (like avoiding unnecessary cloning of large values) are still optional improvements but not required at this stage. - -- **Logging Behavior:** Logging initialization is now robust – it falls back to console logging if the log file can’t be created ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20file_logger_result%20%3D%20File%3A%3Acreate%28file_path%29.map%28%7Cfile%7C%20,clone%28%29%2C%20file%2C%20%29)). Each run truncates the log file on start (so logs don’t grow unbounded across runs). In a long REPL session, logs will accumulate until the session ends, which is expected. There is no log rotation or size limit, but this is fine for the current use-case. The log includes timestamps and retains console output at Info level or higher. No further issues in logging; just keep in mind that if WFL were to be used in a persistent service, a more advanced logging setup might be desired (not urgent now). - -- **Test Coverage:** The test suite has grown to cover the new features and fixes. There are tests for pattern parsing and matching (including named capture extraction) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=if%20let%20Value%3A%3AObject,borrow)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=Value%3A%3AText%28Rc%3A%3Afrom%28)), for proper file I/O behavior (write-then-read, double open errors), for the REPL `.clear` command ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=%5Btokio%3A%3Atest%5D%20async%20fn%20test_clear_command%28%29%20)), and for preventing the environment leak (the second pass tests assumed the leak was fixed) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=match%20at%20L5744%20assert%21%28func)). These tests help ensure that resolved issues stay resolved. It’s recommended to add a test for the environment cycle fix once implemented (e.g., ensure that dropping an interpreter frees all environments or that defining a function in a nested scope doesn’t leak). Overall, test coverage is solid and contributes to confidence in the code’s stability. - -## Review Checklist - -- [ ] **Memory Leaks / RC Cycles:** **Not yet resolved.** The cycle between functions and their defining environment is still present, causing a memory leak ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20function%20%3D%20FunctionValue%20,column%2C)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20function_value%20%3D%20Value%3A%3AFunction%28Rc%3A%3Anew%28function%29%29%3B%20env,clone)). This is a regression from the expected fix. Breaking this cycle via `Weak` references is a priority to reclaim memory from defunct scopes. No other new RC cycles were found in new features (pattern matching creates values but doesn’t introduce lasting cycles). -- [x] **Infinite Loops / Recursion Safety:** **Completed.** Timeouts are applied uniformly to scripts and REPL. The REPL now uses the default 60s execution limit (via `Interpreter::with_timeout` on startup ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=impl%20ReplState%20,timeout_seconds))), so an infinite loop will time out in both script and interactive modes. Deep recursion is likewise bounded by the timeout (and in debug builds an assertion prevents excessive call stack growth). No unbounded recursion or hang was observed in this pass. -- [x] **Data Loss (File I/O):** **Resolved.** File content is no longer erased on open – `IoClient::open_file` uses `.truncate(false)` ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=match%20tokio%3A%3Afs%3A%3AOpenOptions%3A%3Anew%28%29%20,)). The interpreter properly implements `write_file` and `close_file` by calling the `IoClient` and propagating errors ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=%7D%20%3D,await)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=match%20self.io_client.write_file%28%26file_str%2C%20%26content_str%29.await%20)). We verified that writing to a file then reading returns the expected content, and closing a file releases the handle. The prior issues (silent no-ops and data truncation) are fixed. Variables and data in the interpreter are preserved as expected; no runtime data loss issues were found. -- [ ] **Concurrency & Borrowing:** **Flagged (unchanged).** WFL is still single-threaded and sequential. No inherent data races occur under the current design (all code runs on one thread and `RefCell` enforces borrow rules). The file handle management, previously a potential race, has been made race-safe by holding locks during handle cloning and removal ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20mut%20file_handles%20%3D%20self)) ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20mut%20file_handles%20%3D%20self)). If true concurrency is introduced in the future, additional safeguards will be needed (e.g., making the interpreter `Send`, protecting shared state with mutexes, etc.). For now, this item remains noted for future consideration – there’s no concurrency in effect, so no immediate issue beyond the unimplemented parallelism noted above. -- [x] **Unsafe Code / FFI:** **Completed.** The codebase remains free of `unsafe` code. New features (like HTTP via `reqwest` and regex via `regex` crate) are used through safe APIs. All external calls are handled through well-vetted libraries. No FFI or undefined behavior concerns were found. -- [x] **Algorithmic Efficiency:** **Acceptable.** No new algorithms with problematic complexity were added. Pattern matching uses Rust’s regex engine, which is efficient for the given pattern grammar. The interpreter is still a tree-walk execution – adequate for the intended script sizes. Minor inefficiencies (like stringifying values to compare them) are present but not critical. There is currently no evidence of performance bottlenecks in normal use. -- [x] **Error Handling Robustness:** **Mostly completed.** The vast majority of `.unwrap()` calls have been removed or are only present in test code. All I/O operations return `Result` with error messages rather than panicking. The logging initializer handles errors gracefully ([src.md](file://file-EYueVcbiqYsegDiKSjqpB8#:~:text=let%20file_logger_result%20%3D%20File%3A%3Acreate%28file_path%29.map%28%7Cfile%7C%20,clone%28%29%2C%20file%2C%20%29)). One minor place for improvement is the `.clear` command flush (uses `unwrap`), but this is very low impact. Also, as noted, the debug report creation logs errors instead of panicking, which is good – the remaining tweak is to inform the user on failure. Overall, runtime errors are properly caught and turned into `RuntimeError` results; the interpreter doesn’t crash on script errors or bad inputs. -- [x] **Security (Input/Path Handling):** **Completed.** There are no known security vulnerabilities in how WFL handles input or file paths. File paths are used directly as given; with the new behavior, opening a file won’t destroy its contents and will error if already open, reducing unintended side effects. All network access via `http_get`/`http_post` is explicit in scripts – there’s no arbitrary code execution or injection risk within the language itself. (Of course, scripts can perform destructive actions on the host system if run, but that is expected in a language that interfaces with file I/O and the network – running untrusted WFL scripts is not advised unless they are sandboxed externally.) If WFL is ever embedded in a larger application to run untrusted code, additional sandboxing would be needed, but that is beyond the current scope. -- [x] **Logging and Debugging:** **Completed.** Logging no longer panics on setup, and important events are logged with timestamps. The debug report system provides a detailed dump on errors. The only nit is the user message on report failure, as discussed. Logs are flushed on program exit by design; in long runs, data is buffered but can be manually flushed if needed. The debug report includes call stack and local variables, which greatly aids troubleshooting. No logging-related errors were observed during this pass. - -## Suggested Next Steps - -1. **Fix the Function Closure Memory Leak:** Implement the solution to break the `Environment -> Function -> Environment` cycle. The straightforward fix is to change the function’s captured `env` to a `Weak>` and adjust function calls to upgrade that weak reference. This will prevent memory from leaking when environments go out of scope. After implementing, add tests to ensure that defining a function inside another (or in a loop) doesn’t increase memory usage after those scopes end. This is the most crucial fix to make before any long-running usage of WFL. - -2. **Clarify or Enhance Concurrency Features:** Since true parallel execution isn’t implemented, consider updating the documentation or help text to reflect that **`wait for … and …` is sequential** for now. If concurrency is on the roadmap, begin designing how futures would run (e.g., using `tokio::join!` or spawning tasks). This includes making the interpreter state thread-safe or otherwise partitioning state per task. If concurrency is not a near-term goal, it might even be wise to disable the syntax or make `wait for` simply execute both sub-statements back-to-back (to avoid implying parallelism). This will manage user expectations and allow you to introduce real concurrency when ready, without the current “pseudo-concurrency” confusion. - -3. **Document New Features and Behaviors:** Update the README or user guide to cover the newly implemented features (HTTP operations, try/when, pattern matching). Note the behaviors such as `open file` creating files if they don’t exist, the lack of real parallelism, and what the pattern syntax is (e.g., explain that `"3 digits"` or `"{name}"` can be used and that `find_pattern` returns an object of captures). Clear documentation will prevent misuse and bug reports that are really misunderstandings. For example, explicitly stating that WFL is currently single-threaded and that `wait for` is not yet parallel will save users confusion. - -4. **Minor Robustness Polishing:** Tackle the small remaining polish items. For instance, change the debug report message to indicate failure if `create_report` couldn’t write the file (perhaps have `create_report` return a `Result` instead of always `PathBuf`). This is a small change that improves UX. Similarly, you could replace the `.unwrap()` in `.clear` with proper error handling or simply ignore a flush error. These tweaks are low-effort and will make the tool feel more mature and reliable, even in edge cases. - -5. **Improve `Value` Equality (Future Idea):** If the project grows, implementing proper equality for values might be useful (so that `==` in the language or `contains` in a list works intuitively for all types). This could involve adding an `Eq` trait implementation for `Value` or a custom comparison method that handles numbers, text, lists, objects, etc. by content. It’s not urgent for current functionality, but as a forward-looking enhancement it would avoid reliance on string debug representations for comparisons. This goes hand-in-hand with possibly implementing the `Display` trait for `Value` (to differentiate user-facing printouts from `Debug` which is for developers). - -6. **Continue Testing and Monitoring:** After fixing the closure leak and any tweaks, perform another round of targeted testing. In particular, test memory usage by defining many functions in a loop or REPL to confirm no leaks. Also test scenarios with the pattern matching to ensure no panics or unexpected behavior (e.g., malformed patterns should return errors gracefully). Keep an eye on issue reports or user feedback for any performance problems or edge-case errors that weren’t anticipated. Expanding the test suite alongside new features (as you’ve been doing) will maintain confidence. - -By addressing the above, WFL will eliminate its last critical flaw and refine the user experience. The project has come a long way: it now reliably handles file I/O, networking, and error handling in a safe manner. With the memory leak fixed and documentation updated, the focus can shift to new features or performance improvements with a solid foundation in place. Great work so far – resolving this remaining issue will put the project in excellent shape for a 1.0 release. - diff --git a/Cargo.lock b/Cargo.lock index a6ad7dd0..899bfe2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3029,7 +3029,7 @@ dependencies = [ [[package]] name = "wfl" -version = "25.8.23" +version = "25.8.24" dependencies = [ "chrono", "codespan-reporting", diff --git a/Docs/language-reference/loop-scoping.md b/Docs/language-reference/loop-scoping.md new file mode 100644 index 00000000..7e54da41 --- /dev/null +++ b/Docs/language-reference/loop-scoping.md @@ -0,0 +1,88 @@ +# Loop Variable Scoping in WFL + +## Overview + +As of version 25.8.24, WFL implements iteration-scoped loop variables. This means that loop variables are created fresh for each iteration rather than being reused across iterations. + +## Key Behavior + +### Loop Variable Scoping + +In loops (`count from`, `for each`), the loop variable is automatically scoped to each iteration: + +```wfl +count from 1 to 5 as i + display i +end +// Variable 'i' is not accessible here +``` + +### Each Iteration Gets a Fresh Variable + +The loop variable is created anew for each iteration: + +```wfl +count from 1 to 3 as x + store x as x * 2 // This would fail - can't redefine loop variable + display x +end +``` + +## Variable Redefinition Rules + +WFL enforces strict variable redefinition rules: + +1. **Initial Definition**: Use `store` to define a variable for the first time +2. **Subsequent Changes**: Use `change` to modify an existing variable +3. **Loop Variables**: Are automatically managed and cannot be redefined within the loop body + +### Examples + +```wfl +// Correct usage +store name as "Alice" +change name to "Bob" // Must use 'change' for reassignment + +// Incorrect - will produce an error +store age as 25 +store age as 26 // Error: Variable 'age' is already defined + +// Loop variables +count from 1 to 10 as i + // Variable 'i' is read-only within the loop + display i + // store i as 5 // This would fail +end +``` + +## Implementation Details + +The interpreter creates a new scope for each loop iteration and automatically defines the loop variable in that scope. This ensures: + +1. **Isolation**: Each iteration's variables don't interfere with others +2. **Safety**: Prevents accidental variable shadowing +3. **Clarity**: Makes the code's intent more explicit + +## Backward Compatibility + +This change maintains backward compatibility with existing WFL programs. Programs that previously worked will continue to work, as the scoping is more restrictive but doesn't break valid code patterns. + +## Best Practices + +1. Don't try to modify loop variables within the loop body +2. Use descriptive names for loop variables +3. If you need a mutable counter inside a loop, create a separate variable: + +```wfl +store total as 0 +count from 1 to 10 as i + change total to total + i +end +display "Sum: " with total +``` + +## Related Documentation + +- [WFL Variables](wfl-variables.md) +- [Control Flow](wfl-control-flow.md) +- [WFL Specification](wfl-spec.md) \ No newline at end of file diff --git a/Docs/wfl-documentation-index.md b/Docs/wfl-documentation-index.md index 824c63c1..8fb7c17e 100644 --- a/Docs/wfl-documentation-index.md +++ b/Docs/wfl-documentation-index.md @@ -16,6 +16,7 @@ Core language documentation for learning and using WFL: - **[Error Handling](language-reference/wfl-errors.md)** - Understanding and handling errors - **[I/O Operations](language-reference/wfl-io.md)** - File and network input/output - **[Main Loop](language-reference/wfl-main-loop.md)** - Event-driven programming +- **[Loop Scoping](language-reference/loop-scoping.md)** - Loop variable scoping and iteration behavior ## 📖 Guides and Tutorials @@ -120,12 +121,12 @@ When adding new documentation: ## 📊 Documentation Statistics -- **Language Reference:** 10 comprehensive guides +- **Language Reference:** 11 comprehensive guides - **User Guides:** 9 tutorials and how-tos - **API Documentation:** 10 module references - **Technical Docs:** 15 internal documents - **Dev Notes:** 7 development documents -- **Total Documentation:** 51 organized documents +- **Total Documentation:** 52 organized documents *Last updated: August 2025* diff --git a/Nexus/nexus.wfl.lex.txt b/Nexus/nexus.wfl.lex.txt deleted file mode 100644 index 98be2b4d..00000000 --- a/Nexus/nexus.wfl.lex.txt +++ /dev/null @@ -1,1214 +0,0 @@ -Lexer output for: Nexus\nexus.wfl -============================================== - - 0: KeywordOpen at line 10, column 1 (length: 4) - 1: KeywordFile at line 10, column 6 (length: 4) - 2: KeywordAt at line 10, column 11 (length: 2) - 3: StringLiteral("nexus.log") at line 10, column 14 (length: 11) - 4: KeywordAs at line 10, column 26 (length: 2) - 5: Identifier("logHandle") at line 10, column 29 (length: 9) - 6: KeywordDefine at line 13, column 1 (length: 6) - 7: KeywordAction at line 13, column 8 (length: 6) - 8: KeywordCalled at line 13, column 15 (length: 6) - 9: Identifier("log_message") at line 13, column 22 (length: 11) - 10: KeywordNeeds at line 13, column 34 (length: 5) - 11: Identifier("message_text") at line 13, column 40 (length: 12) - 12: Colon at line 13, column 52 (length: 1) - 13: KeywordWait at line 15, column 5 (length: 4) - 14: KeywordFor at line 15, column 10 (length: 3) - 15: KeywordOpen at line 15, column 14 (length: 4) - 16: KeywordFile at line 15, column 19 (length: 4) - 17: KeywordAt at line 15, column 24 (length: 2) - 18: StringLiteral("nexus.log") at line 15, column 27 (length: 11) - 19: KeywordAnd at line 15, column 39 (length: 3) - 20: KeywordRead at line 15, column 43 (length: 4) - 21: KeywordContent at line 15, column 48 (length: 7) - 22: KeywordAs at line 15, column 56 (length: 2) - 23: Identifier("currentLog") at line 15, column 59 (length: 10) - 24: KeywordStore at line 17, column 5 (length: 5) - 25: Identifier("updatedLog") at line 17, column 11 (length: 10) - 26: KeywordAs at line 17, column 22 (length: 2) - 27: Identifier("currentLog") at line 17, column 25 (length: 10) - 28: KeywordWith at line 17, column 36 (length: 4) - 29: Identifier("message_text") at line 17, column 41 (length: 12) - 30: KeywordWith at line 17, column 54 (length: 4) - 31: StringLiteral("\\n") at line 17, column 59 (length: 4) - 32: KeywordWait at line 19, column 5 (length: 4) - 33: KeywordFor at line 19, column 10 (length: 3) - 34: KeywordWrite at line 19, column 14 (length: 5) - 35: KeywordContent at line 19, column 20 (length: 7) - 36: Identifier("updatedLog") at line 19, column 28 (length: 10) - 37: KeywordInto at line 19, column 39 (length: 4) - 38: Identifier("logHandle") at line 19, column 44 (length: 9) - 39: KeywordEnd at line 20, column 1 (length: 3) - 40: KeywordAction at line 20, column 5 (length: 6) - 41: Identifier("log_message") at line 23, column 1 (length: 11) - 42: KeywordWith at line 23, column 13 (length: 4) - 43: StringLiteral("Starting Nexus WFL Integration Test Suite...") at line 23, column 18 (length: 46) - 44: Identifier("log_message") at line 28, column 1 (length: 11) - 45: KeywordWith at line 28, column 13 (length: 4) - 46: StringLiteral("Starting Arithmetic Tests...") at line 28, column 18 (length: 30) - 47: KeywordStore at line 30, column 1 (length: 5) - 48: Identifier("a") at line 30, column 7 (length: 1) - 49: KeywordAs at line 30, column 9 (length: 2) - 50: IntLiteral(6) at line 30, column 12 (length: 1) - 51: KeywordStore at line 31, column 1 (length: 5) - 52: Identifier("b") at line 31, column 7 (length: 1) - 53: KeywordAs at line 31, column 9 (length: 2) - 54: IntLiteral(2) at line 31, column 12 (length: 1) - 55: KeywordStore at line 34, column 1 (length: 5) - 56: Identifier("add_result") at line 34, column 7 (length: 10) - 57: KeywordAs at line 34, column 18 (length: 2) - 58: Identifier("a") at line 34, column 21 (length: 1) - 59: KeywordPlus at line 34, column 23 (length: 4) - 60: Identifier("b") at line 34, column 28 (length: 1) - 61: KeywordCheck at line 35, column 1 (length: 5) - 62: KeywordIf at line 35, column 7 (length: 2) - 63: Identifier("add_result") at line 35, column 10 (length: 10) - 64: KeywordIs at line 35, column 21 (length: 2) - 65: KeywordEqual at line 35, column 24 (length: 5) - 66: KeywordTo at line 35, column 30 (length: 2) - 67: IntLiteral(8) at line 35, column 33 (length: 1) - 68: Colon at line 35, column 34 (length: 1) - 69: Identifier("log_message") at line 36, column 5 (length: 11) - 70: KeywordWith at line 36, column 17 (length: 4) - 71: StringLiteral("Addition test: PASS") at line 36, column 22 (length: 21) - 72: KeywordOtherwise at line 37, column 1 (length: 9) - 73: Colon at line 37, column 10 (length: 1) - 74: Identifier("log_message") at line 38, column 5 (length: 11) - 75: KeywordWith at line 38, column 17 (length: 4) - 76: StringLiteral("Addition test: FAIL (expected 8, got ") at line 38, column 22 (length: 39) - 77: KeywordWith at line 38, column 62 (length: 4) - 78: Identifier("add_result") at line 38, column 67 (length: 10) - 79: KeywordWith at line 38, column 78 (length: 4) - 80: StringLiteral(")") at line 38, column 83 (length: 3) - 81: KeywordEnd at line 39, column 1 (length: 3) - 82: KeywordCheck at line 39, column 5 (length: 5) - 83: KeywordStore at line 42, column 1 (length: 5) - 84: Identifier("sub_result") at line 42, column 7 (length: 10) - 85: KeywordAs at line 42, column 18 (length: 2) - 86: Identifier("a") at line 42, column 21 (length: 1) - 87: KeywordMinus at line 42, column 23 (length: 5) - 88: Identifier("b") at line 42, column 29 (length: 1) - 89: KeywordCheck at line 43, column 1 (length: 5) - 90: KeywordIf at line 43, column 7 (length: 2) - 91: Identifier("sub_result") at line 43, column 10 (length: 10) - 92: KeywordIs at line 43, column 21 (length: 2) - 93: KeywordEqual at line 43, column 24 (length: 5) - 94: KeywordTo at line 43, column 30 (length: 2) - 95: IntLiteral(4) at line 43, column 33 (length: 1) - 96: Colon at line 43, column 34 (length: 1) - 97: Identifier("log_message") at line 44, column 5 (length: 11) - 98: KeywordWith at line 44, column 17 (length: 4) - 99: StringLiteral("Subtraction test: PASS") at line 44, column 22 (length: 24) - 100: KeywordOtherwise at line 45, column 1 (length: 9) - 101: Colon at line 45, column 10 (length: 1) - 102: Identifier("log_message") at line 46, column 5 (length: 11) - 103: KeywordWith at line 46, column 17 (length: 4) - 104: StringLiteral("Subtraction test: FAIL (expected 4, got ") at line 46, column 22 (length: 42) - 105: KeywordWith at line 46, column 65 (length: 4) - 106: Identifier("sub_result") at line 46, column 70 (length: 10) - 107: KeywordWith at line 46, column 81 (length: 4) - 108: StringLiteral(")") at line 46, column 86 (length: 3) - 109: KeywordEnd at line 47, column 1 (length: 3) - 110: KeywordCheck at line 47, column 5 (length: 5) - 111: KeywordStore at line 50, column 1 (length: 5) - 112: Identifier("mul_result") at line 50, column 7 (length: 10) - 113: KeywordAs at line 50, column 18 (length: 2) - 114: Identifier("a") at line 50, column 21 (length: 1) - 115: KeywordTimes at line 50, column 23 (length: 5) - 116: Identifier("b") at line 50, column 29 (length: 1) - 117: KeywordCheck at line 51, column 1 (length: 5) - 118: KeywordIf at line 51, column 7 (length: 2) - 119: Identifier("mul_result") at line 51, column 10 (length: 10) - 120: KeywordIs at line 51, column 21 (length: 2) - 121: KeywordEqual at line 51, column 24 (length: 5) - 122: KeywordTo at line 51, column 30 (length: 2) - 123: IntLiteral(12) at line 51, column 33 (length: 2) - 124: Colon at line 51, column 35 (length: 1) - 125: Identifier("log_message") at line 52, column 5 (length: 11) - 126: KeywordWith at line 52, column 17 (length: 4) - 127: StringLiteral("Multiplication test: PASS") at line 52, column 22 (length: 27) - 128: KeywordOtherwise at line 53, column 1 (length: 9) - 129: Colon at line 53, column 10 (length: 1) - 130: Identifier("log_message") at line 54, column 5 (length: 11) - 131: KeywordWith at line 54, column 17 (length: 4) - 132: StringLiteral("Multiplication test: FAIL (expected 12, got ") at line 54, column 22 (length: 46) - 133: KeywordWith at line 54, column 69 (length: 4) - 134: Identifier("mul_result") at line 54, column 74 (length: 10) - 135: KeywordWith at line 54, column 85 (length: 4) - 136: StringLiteral(")") at line 54, column 90 (length: 3) - 137: KeywordEnd at line 55, column 1 (length: 3) - 138: KeywordCheck at line 55, column 5 (length: 5) - 139: KeywordStore at line 58, column 1 (length: 5) - 140: Identifier("div_result") at line 58, column 7 (length: 10) - 141: KeywordAs at line 58, column 18 (length: 2) - 142: Identifier("a") at line 58, column 21 (length: 1) - 143: KeywordDividedBy at line 58, column 23 (length: 10) - 144: Identifier("b") at line 58, column 34 (length: 1) - 145: KeywordCheck at line 59, column 1 (length: 5) - 146: KeywordIf at line 59, column 7 (length: 2) - 147: Identifier("div_result") at line 59, column 10 (length: 10) - 148: KeywordIs at line 59, column 21 (length: 2) - 149: KeywordEqual at line 59, column 24 (length: 5) - 150: KeywordTo at line 59, column 30 (length: 2) - 151: IntLiteral(3) at line 59, column 33 (length: 1) - 152: Colon at line 59, column 34 (length: 1) - 153: Identifier("log_message") at line 60, column 5 (length: 11) - 154: KeywordWith at line 60, column 17 (length: 4) - 155: StringLiteral("Division test: PASS") at line 60, column 22 (length: 21) - 156: KeywordOtherwise at line 61, column 1 (length: 9) - 157: Colon at line 61, column 10 (length: 1) - 158: Identifier("log_message") at line 62, column 5 (length: 11) - 159: KeywordWith at line 62, column 17 (length: 4) - 160: StringLiteral("Division test: FAIL (expected 3, got ") at line 62, column 22 (length: 39) - 161: KeywordWith at line 62, column 62 (length: 4) - 162: Identifier("div_result") at line 62, column 67 (length: 10) - 163: KeywordWith at line 62, column 78 (length: 4) - 164: StringLiteral(")") at line 62, column 83 (length: 3) - 165: KeywordEnd at line 63, column 1 (length: 3) - 166: KeywordCheck at line 63, column 5 (length: 5) - 167: KeywordStore at line 66, column 1 (length: 5) - 168: Identifier("x") at line 66, column 7 (length: 1) - 169: KeywordAs at line 66, column 9 (length: 2) - 170: IntLiteral(5) at line 66, column 12 (length: 1) - 171: KeywordStore at line 67, column 1 (length: 5) - 172: Identifier("y") at line 67, column 7 (length: 1) - 173: KeywordAs at line 67, column 9 (length: 2) - 174: IntLiteral(2) at line 67, column 12 (length: 1) - 175: KeywordStore at line 68, column 1 (length: 5) - 176: Identifier("frac_result") at line 68, column 7 (length: 11) - 177: KeywordAs at line 68, column 19 (length: 2) - 178: Identifier("x") at line 68, column 22 (length: 1) - 179: KeywordDividedBy at line 68, column 24 (length: 10) - 180: Identifier("y") at line 68, column 35 (length: 1) - 181: KeywordCheck at line 70, column 1 (length: 5) - 182: KeywordIf at line 70, column 7 (length: 2) - 183: Identifier("frac_result") at line 70, column 10 (length: 11) - 184: KeywordTimes at line 70, column 22 (length: 5) - 185: IntLiteral(2) at line 70, column 28 (length: 1) - 186: KeywordIs at line 70, column 30 (length: 2) - 187: KeywordEqual at line 70, column 33 (length: 5) - 188: KeywordTo at line 70, column 39 (length: 2) - 189: Identifier("x") at line 70, column 42 (length: 1) - 190: Colon at line 70, column 43 (length: 1) - 191: Identifier("log_message") at line 71, column 5 (length: 11) - 192: KeywordWith at line 71, column 17 (length: 4) - 193: StringLiteral("Fractional division test: PASS") at line 71, column 22 (length: 32) - 194: KeywordOtherwise at line 72, column 1 (length: 9) - 195: Colon at line 72, column 10 (length: 1) - 196: Identifier("log_message") at line 73, column 5 (length: 11) - 197: KeywordWith at line 73, column 17 (length: 4) - 198: StringLiteral("Fractional division test: FAIL (expected 2.5, got ") at line 73, column 22 (length: 52) - 199: KeywordWith at line 73, column 75 (length: 4) - 200: Identifier("frac_result") at line 73, column 80 (length: 11) - 201: KeywordWith at line 73, column 92 (length: 4) - 202: StringLiteral(")") at line 73, column 97 (length: 3) - 203: KeywordEnd at line 74, column 1 (length: 3) - 204: KeywordCheck at line 74, column 5 (length: 5) - 205: Identifier("log_message") at line 76, column 1 (length: 11) - 206: KeywordWith at line 76, column 13 (length: 4) - 207: StringLiteral("Arithmetic Tests completed.") at line 76, column 18 (length: 29) - 208: Identifier("log_message") at line 81, column 1 (length: 11) - 209: KeywordWith at line 81, column 13 (length: 4) - 210: StringLiteral("Starting Control Flow (If/Else) Tests...") at line 81, column 18 (length: 42) - 211: KeywordStore at line 83, column 1 (length: 5) - 212: Identifier("m") at line 83, column 7 (length: 1) - 213: KeywordAs at line 83, column 9 (length: 2) - 214: IntLiteral(10) at line 83, column 12 (length: 2) - 215: KeywordStore at line 84, column 1 (length: 5) - 216: Identifier("n") at line 84, column 7 (length: 1) - 217: KeywordAs at line 84, column 9 (length: 2) - 218: IntLiteral(5) at line 84, column 12 (length: 1) - 219: KeywordCheck at line 87, column 1 (length: 5) - 220: KeywordIf at line 87, column 7 (length: 2) - 221: Identifier("m") at line 87, column 10 (length: 1) - 222: KeywordIs at line 87, column 12 (length: 2) - 223: KeywordGreater at line 87, column 15 (length: 7) - 224: Identifier("than n") at line 87, column 23 (length: 6) - 225: Colon at line 87, column 29 (length: 1) - 226: KeywordStore at line 88, column 5 (length: 5) - 227: Identifier("result1") at line 88, column 11 (length: 7) - 228: KeywordAs at line 88, column 19 (length: 2) - 229: StringLiteral("yes") at line 88, column 22 (length: 5) - 230: KeywordOtherwise at line 89, column 1 (length: 9) - 231: Colon at line 89, column 10 (length: 1) - 232: KeywordStore at line 90, column 5 (length: 5) - 233: Identifier("result1") at line 90, column 11 (length: 7) - 234: KeywordAs at line 90, column 19 (length: 2) - 235: StringLiteral("no") at line 90, column 22 (length: 4) - 236: KeywordEnd at line 91, column 1 (length: 3) - 237: KeywordCheck at line 91, column 5 (length: 5) - 238: KeywordCheck at line 92, column 1 (length: 5) - 239: KeywordIf at line 92, column 7 (length: 2) - 240: Identifier("result1") at line 92, column 10 (length: 7) - 241: KeywordIs at line 92, column 18 (length: 2) - 242: KeywordEqual at line 92, column 21 (length: 5) - 243: KeywordTo at line 92, column 27 (length: 2) - 244: StringLiteral("yes") at line 92, column 30 (length: 5) - 245: Colon at line 92, column 35 (length: 1) - 246: Identifier("log_message") at line 93, column 5 (length: 11) - 247: KeywordWith at line 93, column 17 (length: 4) - 248: StringLiteral("If condition TRUE branch test: PASS") at line 93, column 22 (length: 37) - 249: KeywordOtherwise at line 94, column 1 (length: 9) - 250: Colon at line 94, column 10 (length: 1) - 251: Identifier("log_message") at line 95, column 5 (length: 11) - 252: KeywordWith at line 95, column 17 (length: 4) - 253: StringLiteral("If condition TRUE branch test: FAIL (expected yes, got ") at line 95, column 22 (length: 57) - 254: KeywordWith at line 95, column 80 (length: 4) - 255: Identifier("result1") at line 95, column 85 (length: 7) - 256: KeywordWith at line 95, column 93 (length: 4) - 257: StringLiteral(")") at line 95, column 98 (length: 3) - 258: KeywordEnd at line 96, column 1 (length: 3) - 259: KeywordCheck at line 96, column 5 (length: 5) - 260: KeywordCheck at line 99, column 1 (length: 5) - 261: KeywordIf at line 99, column 7 (length: 2) - 262: Identifier("m") at line 99, column 10 (length: 1) - 263: KeywordIs at line 99, column 12 (length: 2) - 264: KeywordLess at line 99, column 15 (length: 4) - 265: Identifier("than n") at line 99, column 20 (length: 6) - 266: Colon at line 99, column 26 (length: 1) - 267: KeywordStore at line 100, column 5 (length: 5) - 268: Identifier("result2") at line 100, column 11 (length: 7) - 269: KeywordAs at line 100, column 19 (length: 2) - 270: StringLiteral("yes") at line 100, column 22 (length: 5) - 271: KeywordOtherwise at line 101, column 1 (length: 9) - 272: Colon at line 101, column 10 (length: 1) - 273: KeywordStore at line 102, column 5 (length: 5) - 274: Identifier("result2") at line 102, column 11 (length: 7) - 275: KeywordAs at line 102, column 19 (length: 2) - 276: StringLiteral("no") at line 102, column 22 (length: 4) - 277: KeywordEnd at line 103, column 1 (length: 3) - 278: KeywordCheck at line 103, column 5 (length: 5) - 279: KeywordCheck at line 104, column 1 (length: 5) - 280: KeywordIf at line 104, column 7 (length: 2) - 281: Identifier("result2") at line 104, column 10 (length: 7) - 282: KeywordIs at line 104, column 18 (length: 2) - 283: KeywordEqual at line 104, column 21 (length: 5) - 284: KeywordTo at line 104, column 27 (length: 2) - 285: StringLiteral("no") at line 104, column 30 (length: 4) - 286: Colon at line 104, column 34 (length: 1) - 287: Identifier("log_message") at line 105, column 5 (length: 11) - 288: KeywordWith at line 105, column 17 (length: 4) - 289: StringLiteral("If condition FALSE branch test: PASS") at line 105, column 22 (length: 38) - 290: KeywordOtherwise at line 106, column 1 (length: 9) - 291: Colon at line 106, column 10 (length: 1) - 292: Identifier("log_message") at line 107, column 5 (length: 11) - 293: KeywordWith at line 107, column 17 (length: 4) - 294: StringLiteral("If condition FALSE branch test: FAIL (expected no, got ") at line 107, column 22 (length: 57) - 295: KeywordWith at line 107, column 80 (length: 4) - 296: Identifier("result2") at line 107, column 85 (length: 7) - 297: KeywordWith at line 107, column 93 (length: 4) - 298: StringLiteral(")") at line 107, column 98 (length: 3) - 299: KeywordEnd at line 108, column 1 (length: 3) - 300: KeywordCheck at line 108, column 5 (length: 5) - 301: KeywordStore at line 111, column 1 (length: 5) - 302: Identifier("result3") at line 111, column 7 (length: 7) - 303: KeywordAs at line 111, column 15 (length: 2) - 304: StringLiteral("no") at line 111, column 18 (length: 4) - 305: KeywordCheck at line 112, column 1 (length: 5) - 306: KeywordIf at line 112, column 7 (length: 2) - 307: Identifier("m") at line 112, column 10 (length: 1) - 308: KeywordIs at line 112, column 12 (length: 2) - 309: KeywordGreater at line 112, column 15 (length: 7) - 310: Identifier("than n") at line 112, column 23 (length: 6) - 311: Colon at line 112, column 29 (length: 1) - 312: KeywordChange at line 113, column 5 (length: 6) - 313: Identifier("result3") at line 113, column 12 (length: 7) - 314: KeywordTo at line 113, column 20 (length: 2) - 315: StringLiteral("yes") at line 113, column 23 (length: 5) - 316: KeywordEnd at line 114, column 1 (length: 3) - 317: KeywordCheck at line 114, column 5 (length: 5) - 318: KeywordCheck at line 115, column 1 (length: 5) - 319: KeywordIf at line 115, column 7 (length: 2) - 320: Identifier("result3") at line 115, column 10 (length: 7) - 321: KeywordIs at line 115, column 18 (length: 2) - 322: KeywordEqual at line 115, column 21 (length: 5) - 323: KeywordTo at line 115, column 27 (length: 2) - 324: StringLiteral("yes") at line 115, column 30 (length: 5) - 325: Colon at line 115, column 35 (length: 1) - 326: Identifier("log_message") at line 116, column 5 (length: 11) - 327: KeywordWith at line 116, column 17 (length: 4) - 328: StringLiteral("If (no else) true-case test: PASS") at line 116, column 22 (length: 35) - 329: KeywordOtherwise at line 117, column 1 (length: 9) - 330: Colon at line 117, column 10 (length: 1) - 331: Identifier("log_message") at line 118, column 5 (length: 11) - 332: KeywordWith at line 118, column 17 (length: 4) - 333: StringLiteral("If (no else) true-case test: FAIL") at line 118, column 22 (length: 35) - 334: KeywordEnd at line 119, column 1 (length: 3) - 335: KeywordCheck at line 119, column 5 (length: 5) - 336: KeywordStore at line 122, column 1 (length: 5) - 337: Identifier("result4") at line 122, column 7 (length: 7) - 338: KeywordAs at line 122, column 15 (length: 2) - 339: StringLiteral("yes") at line 122, column 18 (length: 5) - 340: KeywordIf at line 123, column 1 (length: 2) - 341: Identifier("m") at line 123, column 4 (length: 1) - 342: KeywordIs at line 123, column 6 (length: 2) - 343: KeywordEqual at line 123, column 9 (length: 5) - 344: KeywordTo at line 123, column 15 (length: 2) - 345: Identifier("n") at line 123, column 18 (length: 1) - 346: KeywordThen at line 123, column 20 (length: 4) - 347: KeywordChange at line 123, column 25 (length: 6) - 348: Identifier("result4") at line 123, column 32 (length: 7) - 349: KeywordTo at line 123, column 40 (length: 2) - 350: StringLiteral("yes") at line 123, column 43 (length: 5) - 351: KeywordOtherwise at line 123, column 49 (length: 9) - 352: KeywordChange at line 123, column 59 (length: 6) - 353: Identifier("result4") at line 123, column 66 (length: 7) - 354: KeywordTo at line 123, column 74 (length: 2) - 355: StringLiteral("no") at line 123, column 77 (length: 4) - 356: KeywordCheck at line 124, column 1 (length: 5) - 357: KeywordIf at line 124, column 7 (length: 2) - 358: Identifier("result4") at line 124, column 10 (length: 7) - 359: KeywordIs at line 124, column 18 (length: 2) - 360: KeywordEqual at line 124, column 21 (length: 5) - 361: KeywordTo at line 124, column 27 (length: 2) - 362: StringLiteral("no") at line 124, column 30 (length: 4) - 363: Colon at line 124, column 34 (length: 1) - 364: Identifier("log_message") at line 125, column 5 (length: 11) - 365: KeywordWith at line 125, column 17 (length: 4) - 366: StringLiteral("Single-line if/then/otherwise test: PASS") at line 125, column 22 (length: 42) - 367: KeywordOtherwise at line 126, column 1 (length: 9) - 368: Colon at line 126, column 10 (length: 1) - 369: Identifier("log_message") at line 127, column 5 (length: 11) - 370: KeywordWith at line 127, column 17 (length: 4) - 371: StringLiteral("Single-line if/then/otherwise test: FAIL (expected no, got ") at line 127, column 22 (length: 61) - 372: KeywordWith at line 127, column 84 (length: 4) - 373: Identifier("result4") at line 127, column 89 (length: 7) - 374: KeywordWith at line 127, column 97 (length: 4) - 375: StringLiteral(")") at line 127, column 102 (length: 3) - 376: KeywordEnd at line 128, column 1 (length: 3) - 377: KeywordCheck at line 128, column 5 (length: 5) - 378: Identifier("log_message") at line 130, column 1 (length: 11) - 379: KeywordWith at line 130, column 13 (length: 4) - 380: StringLiteral("Control Flow (If/Else) Tests completed.") at line 130, column 18 (length: 41) - 381: Identifier("log_message") at line 135, column 1 (length: 11) - 382: KeywordWith at line 135, column 13 (length: 4) - 383: StringLiteral("Starting Loop Tests...") at line 135, column 18 (length: 24) - 384: KeywordStore at line 138, column 1 (length: 5) - 385: Identifier("sum_count") at line 138, column 7 (length: 9) - 386: KeywordAs at line 138, column 17 (length: 2) - 387: IntLiteral(0) at line 138, column 20 (length: 1) - 388: KeywordCount at line 139, column 1 (length: 5) - 389: KeywordFrom at line 139, column 7 (length: 4) - 390: IntLiteral(1) at line 139, column 12 (length: 1) - 391: KeywordTo at line 139, column 14 (length: 2) - 392: IntLiteral(5) at line 139, column 17 (length: 1) - 393: Colon at line 139, column 18 (length: 1) - 394: KeywordChange at line 140, column 5 (length: 6) - 395: Identifier("sum_count") at line 140, column 12 (length: 9) - 396: KeywordTo at line 140, column 22 (length: 2) - 397: Identifier("sum_count") at line 140, column 25 (length: 9) - 398: KeywordPlus at line 140, column 35 (length: 4) - 399: KeywordCount at line 140, column 40 (length: 5) - 400: KeywordEnd at line 141, column 1 (length: 3) - 401: KeywordCount at line 141, column 5 (length: 5) - 402: KeywordCheck at line 143, column 1 (length: 5) - 403: KeywordIf at line 143, column 7 (length: 2) - 404: Identifier("sum_count") at line 143, column 10 (length: 9) - 405: KeywordIs at line 143, column 20 (length: 2) - 406: KeywordEqual at line 143, column 23 (length: 5) - 407: KeywordTo at line 143, column 29 (length: 2) - 408: IntLiteral(15) at line 143, column 32 (length: 2) - 409: Colon at line 143, column 34 (length: 1) - 410: Identifier("log_message") at line 144, column 5 (length: 11) - 411: KeywordWith at line 144, column 17 (length: 4) - 412: StringLiteral("Count loop test (1 to 5 sum): PASS") at line 144, column 22 (length: 36) - 413: KeywordOtherwise at line 145, column 1 (length: 9) - 414: Colon at line 145, column 10 (length: 1) - 415: Identifier("log_message") at line 146, column 5 (length: 11) - 416: KeywordWith at line 146, column 17 (length: 4) - 417: StringLiteral("Count loop test (expected 15, got ") at line 146, column 22 (length: 36) - 418: KeywordWith at line 146, column 59 (length: 4) - 419: Identifier("sum_count") at line 146, column 64 (length: 9) - 420: KeywordWith at line 146, column 74 (length: 4) - 421: StringLiteral("): FAIL") at line 146, column 79 (length: 9) - 422: KeywordEnd at line 147, column 1 (length: 3) - 423: KeywordCheck at line 147, column 5 (length: 5) - 424: KeywordCreate at line 150, column 1 (length: 6) - 425: Identifier("list") at line 150, column 8 (length: 4) - 426: KeywordAs at line 150, column 13 (length: 2) - 427: Identifier("numbers push") at line 150, column 16 (length: 12) - 428: KeywordWith at line 151, column 6 (length: 4) - 429: Identifier("numbers") at line 151, column 11 (length: 7) - 430: KeywordAnd at line 151, column 19 (length: 3) - 431: IntLiteral(1) at line 151, column 23 (length: 1) - 432: Identifier("push") at line 152, column 1 (length: 4) - 433: KeywordWith at line 152, column 6 (length: 4) - 434: Identifier("numbers") at line 152, column 11 (length: 7) - 435: KeywordAnd at line 152, column 19 (length: 3) - 436: IntLiteral(2) at line 152, column 23 (length: 1) - 437: Identifier("push") at line 153, column 1 (length: 4) - 438: KeywordWith at line 153, column 6 (length: 4) - 439: Identifier("numbers") at line 153, column 11 (length: 7) - 440: KeywordAnd at line 153, column 19 (length: 3) - 441: IntLiteral(3) at line 153, column 23 (length: 1) - 442: KeywordStore at line 155, column 1 (length: 5) - 443: Identifier("sum_for_each") at line 155, column 7 (length: 12) - 444: KeywordAs at line 155, column 20 (length: 2) - 445: IntLiteral(0) at line 155, column 23 (length: 1) - 446: KeywordFor at line 156, column 1 (length: 3) - 447: KeywordEach at line 156, column 5 (length: 4) - 448: Identifier("num") at line 156, column 10 (length: 3) - 449: KeywordIn at line 156, column 14 (length: 2) - 450: Identifier("numbers") at line 156, column 17 (length: 7) - 451: Colon at line 156, column 24 (length: 1) - 452: KeywordChange at line 157, column 5 (length: 6) - 453: Identifier("sum_for_each") at line 157, column 12 (length: 12) - 454: KeywordTo at line 157, column 25 (length: 2) - 455: Identifier("sum_for_each") at line 157, column 28 (length: 12) - 456: KeywordPlus at line 157, column 41 (length: 4) - 457: Identifier("num") at line 157, column 46 (length: 3) - 458: KeywordEnd at line 158, column 1 (length: 3) - 459: KeywordFor at line 158, column 5 (length: 3) - 460: KeywordCheck at line 160, column 1 (length: 5) - 461: KeywordIf at line 160, column 7 (length: 2) - 462: Identifier("sum_for_each") at line 160, column 10 (length: 12) - 463: KeywordIs at line 160, column 23 (length: 2) - 464: KeywordEqual at line 160, column 26 (length: 5) - 465: KeywordTo at line 160, column 32 (length: 2) - 466: IntLiteral(6) at line 160, column 35 (length: 1) - 467: Colon at line 160, column 36 (length: 1) - 468: Identifier("log_message") at line 161, column 5 (length: 11) - 469: KeywordWith at line 161, column 17 (length: 4) - 470: StringLiteral("For-each loop test (sum of [1,2,3]): PASS") at line 161, column 22 (length: 43) - 471: KeywordOtherwise at line 162, column 1 (length: 9) - 472: Colon at line 162, column 10 (length: 1) - 473: Identifier("log_message") at line 163, column 5 (length: 11) - 474: KeywordWith at line 163, column 17 (length: 4) - 475: StringLiteral("For-each loop test (expected 6, got ") at line 163, column 22 (length: 38) - 476: KeywordWith at line 163, column 61 (length: 4) - 477: Identifier("sum_for_each") at line 163, column 66 (length: 12) - 478: KeywordWith at line 163, column 79 (length: 4) - 479: StringLiteral("): FAIL") at line 163, column 84 (length: 9) - 480: KeywordEnd at line 164, column 1 (length: 3) - 481: KeywordCheck at line 164, column 5 (length: 5) - 482: KeywordStore at line 167, column 1 (length: 5) - 483: Identifier("count1") at line 167, column 7 (length: 6) - 484: KeywordAs at line 167, column 14 (length: 2) - 485: IntLiteral(1) at line 167, column 17 (length: 1) - 486: KeywordStore at line 168, column 1 (length: 5) - 487: Identifier("sum_while") at line 168, column 7 (length: 9) - 488: KeywordAs at line 168, column 17 (length: 2) - 489: IntLiteral(0) at line 168, column 20 (length: 1) - 490: KeywordRepeat at line 169, column 1 (length: 6) - 491: KeywordWhile at line 169, column 8 (length: 5) - 492: Identifier("count1") at line 169, column 14 (length: 6) - 493: KeywordIs at line 169, column 21 (length: 2) - 494: KeywordLess at line 169, column 24 (length: 4) - 495: Identifier("than") at line 169, column 29 (length: 4) - 496: KeywordOr at line 169, column 34 (length: 2) - 497: KeywordEqual at line 169, column 37 (length: 5) - 498: KeywordTo at line 169, column 43 (length: 2) - 499: IntLiteral(5) at line 169, column 46 (length: 1) - 500: Colon at line 169, column 47 (length: 1) - 501: KeywordChange at line 170, column 5 (length: 6) - 502: Identifier("sum_while") at line 170, column 12 (length: 9) - 503: KeywordTo at line 170, column 22 (length: 2) - 504: Identifier("sum_while") at line 170, column 25 (length: 9) - 505: KeywordPlus at line 170, column 35 (length: 4) - 506: Identifier("count1") at line 170, column 40 (length: 6) - 507: KeywordChange at line 171, column 5 (length: 6) - 508: Identifier("count1") at line 171, column 12 (length: 6) - 509: KeywordTo at line 171, column 19 (length: 2) - 510: Identifier("count1") at line 171, column 22 (length: 6) - 511: KeywordPlus at line 171, column 29 (length: 4) - 512: IntLiteral(1) at line 171, column 34 (length: 1) - 513: KeywordEnd at line 172, column 1 (length: 3) - 514: KeywordRepeat at line 172, column 5 (length: 6) - 515: KeywordCheck at line 174, column 1 (length: 5) - 516: KeywordIf at line 174, column 7 (length: 2) - 517: Identifier("sum_while") at line 174, column 10 (length: 9) - 518: KeywordIs at line 174, column 20 (length: 2) - 519: KeywordEqual at line 174, column 23 (length: 5) - 520: KeywordTo at line 174, column 29 (length: 2) - 521: IntLiteral(15) at line 174, column 32 (length: 2) - 522: Colon at line 174, column 34 (length: 1) - 523: Identifier("log_message") at line 175, column 5 (length: 11) - 524: KeywordWith at line 175, column 17 (length: 4) - 525: StringLiteral("While loop test (1 to 5 sum): PASS") at line 175, column 22 (length: 36) - 526: KeywordOtherwise at line 176, column 1 (length: 9) - 527: Colon at line 176, column 10 (length: 1) - 528: Identifier("log_message") at line 177, column 5 (length: 11) - 529: KeywordWith at line 177, column 17 (length: 4) - 530: StringLiteral("While loop test (expected 15, got ") at line 177, column 22 (length: 36) - 531: KeywordWith at line 177, column 59 (length: 4) - 532: Identifier("sum_while") at line 177, column 64 (length: 9) - 533: KeywordWith at line 177, column 74 (length: 4) - 534: StringLiteral("): FAIL") at line 177, column 79 (length: 9) - 535: KeywordEnd at line 178, column 1 (length: 3) - 536: KeywordCheck at line 178, column 5 (length: 5) - 537: KeywordStore at line 181, column 1 (length: 5) - 538: Identifier("count2") at line 181, column 7 (length: 6) - 539: KeywordAs at line 181, column 14 (length: 2) - 540: IntLiteral(0) at line 181, column 17 (length: 1) - 541: KeywordStore at line 182, column 1 (length: 5) - 542: Identifier("total_odds") at line 182, column 7 (length: 10) - 543: KeywordAs at line 182, column 18 (length: 2) - 544: IntLiteral(0) at line 182, column 21 (length: 1) - 545: KeywordRepeat at line 183, column 1 (length: 6) - 546: KeywordWhile at line 183, column 8 (length: 5) - 547: Identifier("count2") at line 183, column 14 (length: 6) - 548: KeywordIs at line 183, column 21 (length: 2) - 549: KeywordLess at line 183, column 24 (length: 4) - 550: Identifier("than") at line 183, column 29 (length: 4) - 551: IntLiteral(5) at line 183, column 34 (length: 1) - 552: Colon at line 183, column 35 (length: 1) - 553: KeywordChange at line 184, column 5 (length: 6) - 554: Identifier("count2") at line 184, column 12 (length: 6) - 555: KeywordTo at line 184, column 19 (length: 2) - 556: Identifier("count2") at line 184, column 22 (length: 6) - 557: KeywordPlus at line 184, column 29 (length: 4) - 558: IntLiteral(1) at line 184, column 34 (length: 1) - 559: KeywordCheck at line 186, column 5 (length: 5) - 560: KeywordIf at line 186, column 11 (length: 2) - 561: LeftParen at line 186, column 14 (length: 1) - 562: Identifier("count2") at line 186, column 15 (length: 6) - 563: KeywordDividedBy at line 186, column 22 (length: 10) - 564: IntLiteral(2) at line 186, column 33 (length: 1) - 565: RightParen at line 186, column 34 (length: 1) - 566: KeywordTimes at line 186, column 36 (length: 5) - 567: IntLiteral(2) at line 186, column 42 (length: 1) - 568: KeywordIs at line 186, column 44 (length: 2) - 569: KeywordEqual at line 186, column 47 (length: 5) - 570: KeywordTo at line 186, column 53 (length: 2) - 571: Identifier("count2") at line 186, column 56 (length: 6) - 572: Colon at line 186, column 62 (length: 1) - 573: KeywordSkip at line 187, column 9 (length: 4) - 574: KeywordEnd at line 188, column 5 (length: 3) - 575: KeywordCheck at line 188, column 9 (length: 5) - 576: KeywordChange at line 189, column 5 (length: 6) - 577: Identifier("total_odds") at line 189, column 12 (length: 10) - 578: KeywordTo at line 189, column 23 (length: 2) - 579: Identifier("total_odds") at line 189, column 26 (length: 10) - 580: KeywordPlus at line 189, column 37 (length: 4) - 581: Identifier("count2") at line 189, column 42 (length: 6) - 582: KeywordEnd at line 190, column 1 (length: 3) - 583: KeywordRepeat at line 190, column 5 (length: 6) - 584: KeywordCheck at line 192, column 1 (length: 5) - 585: KeywordIf at line 192, column 7 (length: 2) - 586: Identifier("total_odds") at line 192, column 10 (length: 10) - 587: KeywordIs at line 192, column 21 (length: 2) - 588: KeywordEqual at line 192, column 24 (length: 5) - 589: KeywordTo at line 192, column 30 (length: 2) - 590: IntLiteral(9) at line 192, column 33 (length: 1) - 591: Colon at line 192, column 34 (length: 1) - 592: Identifier("log_message") at line 193, column 5 (length: 11) - 593: KeywordWith at line 193, column 17 (length: 4) - 594: StringLiteral("Loop continue/skip test (sum of odds 1-5): PASS") at line 193, column 22 (length: 49) - 595: KeywordOtherwise at line 194, column 1 (length: 9) - 596: Colon at line 194, column 10 (length: 1) - 597: Identifier("log_message") at line 195, column 5 (length: 11) - 598: KeywordWith at line 195, column 17 (length: 4) - 599: StringLiteral("Loop continue/skip test (expected 9, got ") at line 195, column 22 (length: 43) - 600: KeywordWith at line 195, column 66 (length: 4) - 601: Identifier("total_odds") at line 195, column 71 (length: 10) - 602: KeywordWith at line 195, column 82 (length: 4) - 603: StringLiteral("): FAIL") at line 195, column 87 (length: 9) - 604: KeywordEnd at line 196, column 1 (length: 3) - 605: KeywordCheck at line 196, column 5 (length: 5) - 606: KeywordStore at line 199, column 1 (length: 5) - 607: Identifier("count3") at line 199, column 7 (length: 6) - 608: KeywordAs at line 199, column 14 (length: 2) - 609: IntLiteral(1) at line 199, column 17 (length: 1) - 610: KeywordStore at line 200, column 1 (length: 5) - 611: Identifier("sum_repeat") at line 200, column 7 (length: 10) - 612: KeywordAs at line 200, column 18 (length: 2) - 613: IntLiteral(0) at line 200, column 21 (length: 1) - 614: KeywordRepeat at line 201, column 1 (length: 6) - 615: Colon at line 201, column 7 (length: 1) - 616: KeywordChange at line 202, column 5 (length: 6) - 617: Identifier("sum_repeat") at line 202, column 12 (length: 10) - 618: KeywordTo at line 202, column 23 (length: 2) - 619: Identifier("sum_repeat") at line 202, column 26 (length: 10) - 620: KeywordPlus at line 202, column 37 (length: 4) - 621: Identifier("count3") at line 202, column 42 (length: 6) - 622: KeywordChange at line 203, column 5 (length: 6) - 623: Identifier("count3") at line 203, column 12 (length: 6) - 624: KeywordTo at line 203, column 19 (length: 2) - 625: Identifier("count3") at line 203, column 22 (length: 6) - 626: KeywordPlus at line 203, column 29 (length: 4) - 627: IntLiteral(1) at line 203, column 34 (length: 1) - 628: KeywordUntil at line 204, column 1 (length: 5) - 629: Identifier("count3") at line 204, column 7 (length: 6) - 630: KeywordIs at line 204, column 14 (length: 2) - 631: KeywordGreater at line 204, column 17 (length: 7) - 632: Identifier("than") at line 204, column 25 (length: 4) - 633: IntLiteral(5) at line 204, column 30 (length: 1) - 634: KeywordEnd at line 205, column 1 (length: 3) - 635: KeywordRepeat at line 205, column 5 (length: 6) - 636: KeywordCheck at line 207, column 1 (length: 5) - 637: KeywordIf at line 207, column 7 (length: 2) - 638: Identifier("sum_repeat") at line 207, column 10 (length: 10) - 639: KeywordIs at line 207, column 21 (length: 2) - 640: KeywordEqual at line 207, column 24 (length: 5) - 641: KeywordTo at line 207, column 30 (length: 2) - 642: IntLiteral(15) at line 207, column 33 (length: 2) - 643: Colon at line 207, column 35 (length: 1) - 644: Identifier("log_message") at line 208, column 5 (length: 11) - 645: KeywordWith at line 208, column 17 (length: 4) - 646: StringLiteral("Repeat-until loop test (1 to 5 sum): PASS") at line 208, column 22 (length: 43) - 647: KeywordOtherwise at line 209, column 1 (length: 9) - 648: Colon at line 209, column 10 (length: 1) - 649: Identifier("log_message") at line 210, column 5 (length: 11) - 650: KeywordWith at line 210, column 17 (length: 4) - 651: StringLiteral("Repeat-until loop test (expected 15, got ") at line 210, column 22 (length: 43) - 652: KeywordWith at line 210, column 66 (length: 4) - 653: Identifier("sum_repeat") at line 210, column 71 (length: 10) - 654: KeywordWith at line 210, column 82 (length: 4) - 655: StringLiteral("): FAIL") at line 210, column 87 (length: 9) - 656: KeywordEnd at line 211, column 1 (length: 3) - 657: KeywordCheck at line 211, column 5 (length: 5) - 658: KeywordStore at line 214, column 1 (length: 5) - 659: Identifier("k") at line 214, column 7 (length: 1) - 660: KeywordAs at line 214, column 9 (length: 2) - 661: IntLiteral(0) at line 214, column 12 (length: 1) - 662: KeywordRepeat at line 215, column 1 (length: 6) - 663: KeywordForever at line 215, column 8 (length: 7) - 664: Colon at line 215, column 15 (length: 1) - 665: KeywordChange at line 216, column 5 (length: 6) - 666: Identifier("k") at line 216, column 12 (length: 1) - 667: KeywordTo at line 216, column 14 (length: 2) - 668: Identifier("k") at line 216, column 17 (length: 1) - 669: KeywordPlus at line 216, column 19 (length: 4) - 670: IntLiteral(1) at line 216, column 24 (length: 1) - 671: KeywordCheck at line 217, column 5 (length: 5) - 672: KeywordIf at line 217, column 11 (length: 2) - 673: Identifier("k") at line 217, column 14 (length: 1) - 674: KeywordIs at line 217, column 16 (length: 2) - 675: KeywordEqual at line 217, column 19 (length: 5) - 676: KeywordTo at line 217, column 25 (length: 2) - 677: IntLiteral(5) at line 217, column 28 (length: 1) - 678: Colon at line 217, column 29 (length: 1) - 679: KeywordBreak at line 218, column 9 (length: 5) - 680: KeywordEnd at line 219, column 5 (length: 3) - 681: KeywordCheck at line 219, column 9 (length: 5) - 682: KeywordEnd at line 220, column 1 (length: 3) - 683: KeywordRepeat at line 220, column 5 (length: 6) - 684: KeywordCheck at line 221, column 1 (length: 5) - 685: KeywordIf at line 221, column 7 (length: 2) - 686: Identifier("k") at line 221, column 10 (length: 1) - 687: KeywordIs at line 221, column 12 (length: 2) - 688: KeywordEqual at line 221, column 15 (length: 5) - 689: KeywordTo at line 221, column 21 (length: 2) - 690: IntLiteral(5) at line 221, column 24 (length: 1) - 691: Colon at line 221, column 25 (length: 1) - 692: Identifier("log_message") at line 222, column 5 (length: 11) - 693: KeywordWith at line 222, column 17 (length: 4) - 694: StringLiteral("Forever loop with break test: PASS") at line 222, column 22 (length: 36) - 695: KeywordOtherwise at line 223, column 1 (length: 9) - 696: Colon at line 223, column 10 (length: 1) - 697: Identifier("log_message") at line 224, column 5 (length: 11) - 698: KeywordWith at line 224, column 17 (length: 4) - 699: StringLiteral("Forever loop with break test: FAIL (k = ") at line 224, column 22 (length: 42) - 700: KeywordWith at line 224, column 65 (length: 4) - 701: Identifier("k") at line 224, column 70 (length: 1) - 702: KeywordWith at line 224, column 72 (length: 4) - 703: StringLiteral(")") at line 224, column 77 (length: 3) - 704: KeywordEnd at line 225, column 1 (length: 3) - 705: KeywordCheck at line 225, column 5 (length: 5) - 706: KeywordStore at line 228, column 1 (length: 5) - 707: Identifier("break_outer_counter") at line 228, column 7 (length: 19) - 708: KeywordAs at line 228, column 27 (length: 2) - 709: IntLiteral(0) at line 228, column 30 (length: 1) - 710: KeywordCount at line 229, column 1 (length: 5) - 711: KeywordFrom at line 229, column 7 (length: 4) - 712: IntLiteral(1) at line 229, column 12 (length: 1) - 713: KeywordTo at line 229, column 14 (length: 2) - 714: IntLiteral(3) at line 229, column 17 (length: 1) - 715: Colon at line 229, column 18 (length: 1) - 716: KeywordCount at line 230, column 5 (length: 5) - 717: KeywordFrom at line 230, column 11 (length: 4) - 718: IntLiteral(1) at line 230, column 16 (length: 1) - 719: KeywordTo at line 230, column 18 (length: 2) - 720: IntLiteral(3) at line 230, column 21 (length: 1) - 721: Colon at line 230, column 22 (length: 1) - 722: KeywordCheck at line 231, column 9 (length: 5) - 723: KeywordIf at line 231, column 15 (length: 2) - 724: KeywordCount at line 231, column 18 (length: 5) - 725: KeywordIs at line 231, column 24 (length: 2) - 726: KeywordEqual at line 231, column 27 (length: 5) - 727: KeywordTo at line 231, column 33 (length: 2) - 728: IntLiteral(2) at line 231, column 36 (length: 1) - 729: Colon at line 231, column 37 (length: 1) - 730: KeywordBreak at line 232, column 13 (length: 5) - 731: KeywordEnd at line 233, column 9 (length: 3) - 732: KeywordCheck at line 233, column 13 (length: 5) - 733: KeywordEnd at line 234, column 5 (length: 3) - 734: KeywordCount at line 234, column 9 (length: 5) - 735: KeywordChange at line 235, column 5 (length: 6) - 736: Identifier("break_outer_counter") at line 235, column 12 (length: 19) - 737: KeywordTo at line 235, column 32 (length: 2) - 738: Identifier("break_outer_counter") at line 235, column 35 (length: 19) - 739: KeywordPlus at line 235, column 55 (length: 4) - 740: IntLiteral(1) at line 235, column 60 (length: 1) - 741: KeywordEnd at line 236, column 1 (length: 3) - 742: KeywordCount at line 236, column 5 (length: 5) - 743: KeywordCheck at line 238, column 1 (length: 5) - 744: KeywordIf at line 238, column 7 (length: 2) - 745: Identifier("break_outer_counter") at line 238, column 10 (length: 19) - 746: KeywordIs at line 238, column 30 (length: 2) - 747: KeywordEqual at line 238, column 33 (length: 5) - 748: KeywordTo at line 238, column 39 (length: 2) - 749: IntLiteral(3) at line 238, column 42 (length: 1) - 750: Colon at line 238, column 43 (length: 1) - 751: Identifier("log_message") at line 239, column 5 (length: 11) - 752: KeywordWith at line 239, column 17 (length: 4) - 753: StringLiteral("Nested loop 'break' test: PASS") at line 239, column 22 (length: 32) - 754: KeywordOtherwise at line 240, column 1 (length: 9) - 755: Colon at line 240, column 10 (length: 1) - 756: Identifier("log_message") at line 241, column 5 (length: 11) - 757: KeywordWith at line 241, column 17 (length: 4) - 758: StringLiteral("Nested loop 'break' test: FAIL (outer iterations = ") at line 241, column 22 (length: 53) - 759: KeywordWith at line 241, column 76 (length: 4) - 760: Identifier("break_outer_counter") at line 241, column 81 (length: 19) - 761: KeywordWith at line 241, column 101 (length: 4) - 762: StringLiteral(")") at line 241, column 106 (length: 3) - 763: KeywordEnd at line 242, column 1 (length: 3) - 764: KeywordCheck at line 242, column 5 (length: 5) - 765: KeywordStore at line 244, column 1 (length: 5) - 766: Identifier("exit_outer_counter") at line 244, column 7 (length: 18) - 767: KeywordAs at line 244, column 26 (length: 2) - 768: IntLiteral(0) at line 244, column 29 (length: 1) - 769: KeywordCount at line 245, column 1 (length: 5) - 770: KeywordFrom at line 245, column 7 (length: 4) - 771: IntLiteral(1) at line 245, column 12 (length: 1) - 772: KeywordTo at line 245, column 14 (length: 2) - 773: IntLiteral(3) at line 245, column 17 (length: 1) - 774: Colon at line 245, column 18 (length: 1) - 775: KeywordCount at line 246, column 5 (length: 5) - 776: KeywordFrom at line 246, column 11 (length: 4) - 777: IntLiteral(1) at line 246, column 16 (length: 1) - 778: KeywordTo at line 246, column 18 (length: 2) - 779: IntLiteral(3) at line 246, column 21 (length: 1) - 780: Colon at line 246, column 22 (length: 1) - 781: KeywordCheck at line 247, column 9 (length: 5) - 782: KeywordIf at line 247, column 15 (length: 2) - 783: KeywordCount at line 247, column 18 (length: 5) - 784: KeywordIs at line 247, column 24 (length: 2) - 785: KeywordEqual at line 247, column 27 (length: 5) - 786: KeywordTo at line 247, column 33 (length: 2) - 787: IntLiteral(2) at line 247, column 36 (length: 1) - 788: Colon at line 247, column 37 (length: 1) - 789: KeywordExit at line 248, column 13 (length: 4) - 790: KeywordLoop at line 248, column 18 (length: 4) - 791: KeywordEnd at line 249, column 9 (length: 3) - 792: KeywordCheck at line 249, column 13 (length: 5) - 793: KeywordEnd at line 250, column 5 (length: 3) - 794: KeywordCount at line 250, column 9 (length: 5) - 795: KeywordChange at line 252, column 5 (length: 6) - 796: Identifier("exit_outer_counter") at line 252, column 12 (length: 18) - 797: KeywordTo at line 252, column 31 (length: 2) - 798: Identifier("exit_outer_counter") at line 252, column 34 (length: 18) - 799: KeywordPlus at line 252, column 53 (length: 4) - 800: IntLiteral(1) at line 252, column 58 (length: 1) - 801: KeywordEnd at line 253, column 1 (length: 3) - 802: KeywordCount at line 253, column 5 (length: 5) - 803: KeywordCheck at line 255, column 1 (length: 5) - 804: KeywordIf at line 255, column 7 (length: 2) - 805: Identifier("exit_outer_counter") at line 255, column 10 (length: 18) - 806: KeywordIs at line 255, column 29 (length: 2) - 807: KeywordEqual at line 255, column 32 (length: 5) - 808: KeywordTo at line 255, column 38 (length: 2) - 809: IntLiteral(1) at line 255, column 41 (length: 1) - 810: Colon at line 255, column 42 (length: 1) - 811: Identifier("log_message") at line 256, column 5 (length: 11) - 812: KeywordWith at line 256, column 17 (length: 4) - 813: StringLiteral("Nested loop 'exit' test: PASS") at line 256, column 22 (length: 31) - 814: KeywordOtherwise at line 257, column 1 (length: 9) - 815: Colon at line 257, column 10 (length: 1) - 816: Identifier("log_message") at line 258, column 5 (length: 11) - 817: KeywordWith at line 258, column 17 (length: 4) - 818: StringLiteral("Nested loop 'exit' test: FAIL (outer iterations = ") at line 258, column 22 (length: 52) - 819: KeywordWith at line 258, column 75 (length: 4) - 820: Identifier("exit_outer_counter") at line 258, column 80 (length: 18) - 821: KeywordWith at line 258, column 99 (length: 4) - 822: StringLiteral(")") at line 258, column 104 (length: 3) - 823: KeywordEnd at line 259, column 1 (length: 3) - 824: KeywordCheck at line 259, column 5 (length: 5) - 825: Identifier("log_message") at line 261, column 1 (length: 11) - 826: KeywordWith at line 261, column 13 (length: 4) - 827: StringLiteral("Loop Tests completed.") at line 261, column 18 (length: 23) - 828: Identifier("log_message") at line 266, column 1 (length: 11) - 829: KeywordWith at line 266, column 13 (length: 4) - 830: StringLiteral("Starting Action/Function Tests...") at line 266, column 18 (length: 35) - 831: KeywordDefine at line 271, column 1 (length: 6) - 832: KeywordAction at line 271, column 8 (length: 6) - 833: KeywordCalled at line 271, column 15 (length: 6) - 834: Identifier("greet") at line 271, column 22 (length: 5) - 835: Colon at line 271, column 27 (length: 1) - 836: KeywordDisplay at line 273, column 5 (length: 7) - 837: StringLiteral("Hello, World from WFL!") at line 273, column 13 (length: 24) - 838: KeywordEnd at line 274, column 1 (length: 3) - 839: KeywordAction at line 274, column 5 (length: 6) - 840: KeywordDefine at line 277, column 1 (length: 6) - 841: KeywordAction at line 277, column 8 (length: 6) - 842: KeywordCalled at line 277, column 15 (length: 6) - 843: Identifier("square") at line 277, column 22 (length: 6) - 844: KeywordNeeds at line 277, column 29 (length: 5) - 845: Identifier("value") at line 277, column 35 (length: 5) - 846: Colon at line 277, column 40 (length: 1) - 847: KeywordGive at line 278, column 5 (length: 4) - 848: KeywordBack at line 278, column 10 (length: 4) - 849: Identifier("value") at line 278, column 15 (length: 5) - 850: KeywordTimes at line 278, column 21 (length: 5) - 851: Identifier("value") at line 278, column 27 (length: 5) - 852: KeywordEnd at line 279, column 1 (length: 3) - 853: KeywordAction at line 279, column 5 (length: 6) - 854: KeywordDefine at line 282, column 1 (length: 6) - 855: KeywordAction at line 282, column 8 (length: 6) - 856: KeywordCalled at line 282, column 15 (length: 6) - 857: Identifier("add") at line 282, column 22 (length: 3) - 858: KeywordNeeds at line 282, column 26 (length: 5) - 859: Identifier("p") at line 282, column 32 (length: 1) - 860: KeywordAnd at line 282, column 34 (length: 3) - 861: Identifier("q") at line 282, column 38 (length: 1) - 862: Colon at line 282, column 39 (length: 1) - 863: KeywordGive at line 283, column 5 (length: 4) - 864: KeywordBack at line 283, column 10 (length: 4) - 865: Identifier("p") at line 283, column 15 (length: 1) - 866: KeywordPlus at line 283, column 17 (length: 4) - 867: Identifier("q") at line 283, column 22 (length: 1) - 868: KeywordEnd at line 284, column 1 (length: 3) - 869: KeywordAction at line 284, column 5 (length: 6) - 870: KeywordDefine at line 287, column 1 (length: 6) - 871: KeywordAction at line 287, column 8 (length: 6) - 872: KeywordCalled at line 287, column 15 (length: 6) - 873: Identifier("factorial") at line 287, column 22 (length: 9) - 874: KeywordNeeds at line 287, column 32 (length: 5) - 875: Identifier("n") at line 287, column 38 (length: 1) - 876: Colon at line 287, column 39 (length: 1) - 877: KeywordCheck at line 288, column 5 (length: 5) - 878: KeywordIf at line 288, column 11 (length: 2) - 879: Identifier("n") at line 288, column 14 (length: 1) - 880: KeywordIs at line 288, column 16 (length: 2) - 881: KeywordEqual at line 288, column 19 (length: 5) - 882: KeywordTo at line 288, column 25 (length: 2) - 883: IntLiteral(0) at line 288, column 28 (length: 1) - 884: Colon at line 288, column 29 (length: 1) - 885: KeywordGive at line 289, column 9 (length: 4) - 886: KeywordBack at line 289, column 14 (length: 4) - 887: IntLiteral(1) at line 289, column 19 (length: 1) - 888: KeywordOtherwise at line 290, column 5 (length: 9) - 889: Colon at line 290, column 14 (length: 1) - 890: KeywordGive at line 292, column 9 (length: 4) - 891: KeywordBack at line 292, column 14 (length: 4) - 892: Identifier("n") at line 292, column 19 (length: 1) - 893: KeywordTimes at line 292, column 21 (length: 5) - 894: Identifier("factorial") at line 292, column 27 (length: 9) - 895: KeywordWith at line 292, column 37 (length: 4) - 896: Identifier("n") at line 292, column 42 (length: 1) - 897: KeywordMinus at line 292, column 44 (length: 5) - 898: IntLiteral(1) at line 292, column 50 (length: 1) - 899: KeywordEnd at line 293, column 5 (length: 3) - 900: KeywordCheck at line 293, column 9 (length: 5) - 901: KeywordEnd at line 294, column 1 (length: 3) - 902: KeywordAction at line 294, column 5 (length: 6) - 903: KeywordDefine at line 297, column 1 (length: 6) - 904: KeywordAction at line 297, column 8 (length: 6) - 905: KeywordCalled at line 297, column 15 (length: 6) - 906: Identifier("faulty") at line 297, column 22 (length: 6) - 907: Colon at line 297, column 28 (length: 1) - 908: KeywordStore at line 299, column 5 (length: 5) - 909: Identifier("u") at line 299, column 11 (length: 1) - 910: KeywordAs at line 299, column 13 (length: 2) - 911: IntLiteral(1) at line 299, column 16 (length: 1) - 912: KeywordStore at line 300, column 5 (length: 5) - 913: Identifier("v") at line 300, column 11 (length: 1) - 914: KeywordAs at line 300, column 13 (length: 2) - 915: IntLiteral(0) at line 300, column 16 (length: 1) - 916: KeywordStore at line 301, column 5 (length: 5) - 917: Identifier("w") at line 301, column 11 (length: 1) - 918: KeywordAs at line 301, column 13 (length: 2) - 919: Identifier("u") at line 301, column 16 (length: 1) - 920: KeywordDividedBy at line 301, column 18 (length: 10) - 921: Identifier("v") at line 301, column 29 (length: 1) - 922: KeywordGive at line 302, column 5 (length: 4) - 923: KeywordBack at line 302, column 10 (length: 4) - 924: Identifier("w") at line 302, column 15 (length: 1) - 925: KeywordEnd at line 303, column 1 (length: 3) - 926: KeywordAction at line 303, column 5 (length: 6) - 927: Identifier("greet") at line 308, column 1 (length: 5) - 928: KeywordStore at line 311, column 1 (length: 5) - 929: Identifier("sq_result") at line 311, column 7 (length: 9) - 930: KeywordAs at line 311, column 17 (length: 2) - 931: Identifier("square") at line 311, column 20 (length: 6) - 932: KeywordWith at line 311, column 27 (length: 4) - 933: IntLiteral(4) at line 311, column 32 (length: 1) - 934: KeywordCheck at line 312, column 1 (length: 5) - 935: KeywordIf at line 312, column 7 (length: 2) - 936: Identifier("sq_result") at line 312, column 10 (length: 9) - 937: KeywordIs at line 312, column 20 (length: 2) - 938: KeywordEqual at line 312, column 23 (length: 5) - 939: KeywordTo at line 312, column 29 (length: 2) - 940: IntLiteral(16) at line 312, column 32 (length: 2) - 941: Colon at line 312, column 34 (length: 1) - 942: Identifier("log_message") at line 313, column 5 (length: 11) - 943: KeywordWith at line 313, column 17 (length: 4) - 944: StringLiteral("Action test (square 4 -> 16): PASS") at line 313, column 22 (length: 36) - 945: KeywordOtherwise at line 314, column 1 (length: 9) - 946: Colon at line 314, column 10 (length: 1) - 947: Identifier("log_message") at line 315, column 5 (length: 11) - 948: KeywordWith at line 315, column 17 (length: 4) - 949: StringLiteral("Action test (square) FAIL (expected 16, got ") at line 315, column 22 (length: 46) - 950: KeywordWith at line 315, column 69 (length: 4) - 951: Identifier("sq_result") at line 315, column 74 (length: 9) - 952: KeywordWith at line 315, column 84 (length: 4) - 953: StringLiteral(")") at line 315, column 89 (length: 3) - 954: KeywordEnd at line 316, column 1 (length: 3) - 955: KeywordCheck at line 316, column 5 (length: 5) - 956: KeywordStore at line 319, column 1 (length: 5) - 957: Identifier("add_result2") at line 319, column 7 (length: 11) - 958: KeywordAs at line 319, column 19 (length: 2) - 959: Identifier("add") at line 319, column 22 (length: 3) - 960: KeywordWith at line 319, column 26 (length: 4) - 961: IntLiteral(10) at line 319, column 31 (length: 2) - 962: KeywordAnd at line 319, column 34 (length: 3) - 963: IntLiteral(15) at line 319, column 38 (length: 2) - 964: KeywordCheck at line 320, column 1 (length: 5) - 965: KeywordIf at line 320, column 7 (length: 2) - 966: Identifier("add_result2") at line 320, column 10 (length: 11) - 967: KeywordIs at line 320, column 22 (length: 2) - 968: KeywordEqual at line 320, column 25 (length: 5) - 969: KeywordTo at line 320, column 31 (length: 2) - 970: IntLiteral(25) at line 320, column 34 (length: 2) - 971: Colon at line 320, column 36 (length: 1) - 972: Identifier("log_message") at line 321, column 5 (length: 11) - 973: KeywordWith at line 321, column 17 (length: 4) - 974: StringLiteral("Action test (add 10+15 -> 25): PASS") at line 321, column 22 (length: 37) - 975: KeywordOtherwise at line 322, column 1 (length: 9) - 976: Colon at line 322, column 10 (length: 1) - 977: Identifier("log_message") at line 323, column 5 (length: 11) - 978: KeywordWith at line 323, column 17 (length: 4) - 979: StringLiteral("Action test (add) FAIL (expected 25, got ") at line 323, column 22 (length: 43) - 980: KeywordWith at line 323, column 66 (length: 4) - 981: Identifier("add_result2") at line 323, column 71 (length: 11) - 982: KeywordWith at line 323, column 83 (length: 4) - 983: StringLiteral(")") at line 323, column 88 (length: 3) - 984: KeywordEnd at line 324, column 1 (length: 3) - 985: KeywordCheck at line 324, column 5 (length: 5) - 986: KeywordStore at line 327, column 1 (length: 5) - 987: Identifier("fact_result") at line 327, column 7 (length: 11) - 988: KeywordAs at line 327, column 19 (length: 2) - 989: Identifier("factorial") at line 327, column 22 (length: 9) - 990: KeywordWith at line 327, column 32 (length: 4) - 991: IntLiteral(5) at line 327, column 37 (length: 1) - 992: KeywordCheck at line 328, column 1 (length: 5) - 993: KeywordIf at line 328, column 7 (length: 2) - 994: Identifier("fact_result") at line 328, column 10 (length: 11) - 995: KeywordIs at line 328, column 22 (length: 2) - 996: KeywordEqual at line 328, column 25 (length: 5) - 997: KeywordTo at line 328, column 31 (length: 2) - 998: IntLiteral(120) at line 328, column 34 (length: 3) - 999: Colon at line 328, column 37 (length: 1) -1000: Identifier("log_message") at line 329, column 5 (length: 11) -1001: KeywordWith at line 329, column 17 (length: 4) -1002: StringLiteral("Action test (factorial 5 -> 120): PASS") at line 329, column 22 (length: 40) -1003: KeywordOtherwise at line 330, column 1 (length: 9) -1004: Colon at line 330, column 10 (length: 1) -1005: Identifier("log_message") at line 331, column 5 (length: 11) -1006: KeywordWith at line 331, column 17 (length: 4) -1007: StringLiteral("Action test (factorial) FAIL (expected 120, got ") at line 331, column 22 (length: 50) -1008: KeywordWith at line 331, column 73 (length: 4) -1009: Identifier("fact_result") at line 331, column 78 (length: 11) -1010: KeywordWith at line 331, column 90 (length: 4) -1011: StringLiteral(")") at line 331, column 95 (length: 3) -1012: KeywordEnd at line 332, column 1 (length: 3) -1013: KeywordCheck at line 332, column 5 (length: 5) -1014: KeywordTry at line 335, column 1 (length: 3) -1015: Colon at line 335, column 4 (length: 1) -1016: KeywordStore at line 337, column 5 (length: 5) -1017: Identifier("res") at line 337, column 11 (length: 3) -1018: KeywordAs at line 337, column 15 (length: 2) -1019: Identifier("faulty log_message") at line 337, column 18 (length: 18) -1020: KeywordWith at line 339, column 17 (length: 4) -1021: StringLiteral("Error handling test: FAIL (no error from faulty action)") at line 339, column 22 (length: 57) -1022: KeywordWhen at line 340, column 1 (length: 4) -1023: KeywordError at line 340, column 6 (length: 5) -1024: Colon at line 340, column 11 (length: 1) -1025: KeywordDisplay at line 342, column 5 (length: 7) -1026: StringLiteral("Caught expected error: ") at line 342, column 13 (length: 25) -1027: KeywordWith at line 342, column 39 (length: 4) -1028: KeywordError at line 342, column 44 (length: 5) -1029: Identifier("log_message") at line 343, column 5 (length: 11) -1030: KeywordWith at line 343, column 17 (length: 4) -1031: StringLiteral("Error handling test: PASS (caught error: ") at line 343, column 22 (length: 43) -1032: KeywordWith at line 343, column 66 (length: 4) -1033: KeywordError at line 343, column 71 (length: 5) -1034: KeywordWith at line 343, column 77 (length: 4) -1035: StringLiteral(")") at line 343, column 82 (length: 3) -1036: KeywordEnd at line 344, column 1 (length: 3) -1037: KeywordTry at line 344, column 5 (length: 3) -1038: Identifier("log_message") at line 346, column 1 (length: 11) -1039: KeywordWith at line 346, column 13 (length: 4) -1040: StringLiteral("Action/Function Tests completed.") at line 346, column 18 (length: 34) -1041: Identifier("log_message") at line 351, column 1 (length: 11) -1042: KeywordWith at line 351, column 13 (length: 4) -1043: StringLiteral("Starting Pattern Matching Tests...") at line 351, column 18 (length: 36) -1044: KeywordStore at line 354, column 1 (length: 5) -1045: Identifier("pat") at line 354, column 7 (length: 3) -1046: KeywordAs at line 354, column 11 (length: 2) -1047: KeywordPattern at line 354, column 14 (length: 7) -1048: StringLiteral("3 digits") at line 354, column 22 (length: 10) -1049: KeywordStore at line 357, column 1 (length: 5) -1050: Identifier("text1") at line 357, column 7 (length: 5) -1051: KeywordAs at line 357, column 13 (length: 2) -1052: StringLiteral("abc123xyz") at line 357, column 16 (length: 11) -1053: KeywordCheck at line 358, column 1 (length: 5) -1054: KeywordIf at line 358, column 7 (length: 2) -1055: Identifier("text1") at line 358, column 10 (length: 5) -1056: KeywordContains at line 358, column 16 (length: 8) -1057: Identifier("pat") at line 358, column 25 (length: 3) -1058: Colon at line 358, column 28 (length: 1) -1059: Identifier("log_message") at line 359, column 5 (length: 11) -1060: KeywordWith at line 359, column 17 (length: 4) -1061: StringLiteral("Pattern test (\"abc123xyz\" contains 3 digits): PASS") at line 359, column 22 (length: 54) -1062: KeywordOtherwise at line 360, column 1 (length: 9) -1063: Colon at line 360, column 10 (length: 1) -1064: Identifier("log_message") at line 361, column 5 (length: 11) -1065: KeywordWith at line 361, column 17 (length: 4) -1066: StringLiteral("Pattern test (\"abc123xyz\" should contain 3 digits): FAIL") at line 361, column 22 (length: 60) -1067: KeywordEnd at line 362, column 1 (length: 3) -1068: KeywordCheck at line 362, column 5 (length: 5) -1069: KeywordStore at line 365, column 1 (length: 5) -1070: Identifier("text2") at line 365, column 7 (length: 5) -1071: KeywordAs at line 365, column 13 (length: 2) -1072: StringLiteral("abc45xyz") at line 365, column 16 (length: 10) -1073: KeywordCheck at line 366, column 1 (length: 5) -1074: KeywordIf at line 366, column 7 (length: 2) -1075: Identifier("text2") at line 366, column 10 (length: 5) -1076: KeywordContains at line 366, column 16 (length: 8) -1077: Identifier("pat") at line 366, column 25 (length: 3) -1078: Colon at line 366, column 28 (length: 1) -1079: Identifier("log_message") at line 367, column 5 (length: 11) -1080: KeywordWith at line 367, column 17 (length: 4) -1081: StringLiteral("Pattern test (\"abc45xyz\" should NOT contain 3 digits): FAIL") at line 367, column 22 (length: 63) -1082: KeywordOtherwise at line 368, column 1 (length: 9) -1083: Colon at line 368, column 10 (length: 1) -1084: Identifier("log_message") at line 369, column 5 (length: 11) -1085: KeywordWith at line 369, column 17 (length: 4) -1086: StringLiteral("Pattern test (\"abc45xyz\" no 3-digit sequence): PASS") at line 369, column 22 (length: 55) -1087: KeywordEnd at line 370, column 1 (length: 3) -1088: KeywordCheck at line 370, column 5 (length: 5) -1089: Identifier("log_message") at line 372, column 1 (length: 11) -1090: KeywordWith at line 372, column 13 (length: 4) -1091: StringLiteral("Pattern Matching Tests completed.") at line 372, column 18 (length: 35) -1092: Identifier("log_message") at line 377, column 1 (length: 11) -1093: KeywordWith at line 377, column 13 (length: 4) -1094: StringLiteral("Starting Async I/O and Concurrency Tests...") at line 377, column 18 (length: 45) -1095: KeywordOpen at line 380, column 1 (length: 4) -1096: KeywordFile at line 380, column 6 (length: 4) -1097: KeywordAt at line 380, column 11 (length: 2) -1098: StringLiteral("temp1.txt") at line 380, column 14 (length: 11) -1099: KeywordAs at line 380, column 26 (length: 2) -1100: Identifier("file1") at line 380, column 29 (length: 5) -1101: KeywordWait at line 381, column 1 (length: 4) -1102: KeywordFor at line 381, column 6 (length: 3) -1103: KeywordWrite at line 381, column 10 (length: 5) -1104: KeywordContent at line 381, column 16 (length: 7) -1105: StringLiteral("FileOneContent") at line 381, column 24 (length: 16) -1106: KeywordInto at line 381, column 41 (length: 4) -1107: Identifier("file1") at line 381, column 46 (length: 5) -1108: KeywordClose at line 382, column 1 (length: 5) -1109: KeywordFile at line 382, column 7 (length: 4) -1110: Identifier("file1") at line 382, column 12 (length: 5) -1111: KeywordOpen at line 384, column 1 (length: 4) -1112: KeywordFile at line 384, column 6 (length: 4) -1113: KeywordAt at line 384, column 11 (length: 2) -1114: StringLiteral("temp2.txt") at line 384, column 14 (length: 11) -1115: KeywordAs at line 384, column 26 (length: 2) -1116: Identifier("file2") at line 384, column 29 (length: 5) -1117: KeywordWait at line 385, column 1 (length: 4) -1118: KeywordFor at line 385, column 6 (length: 3) -1119: KeywordWrite at line 385, column 10 (length: 5) -1120: KeywordContent at line 385, column 16 (length: 7) -1121: StringLiteral("FileTwoContent") at line 385, column 24 (length: 16) -1122: KeywordInto at line 385, column 41 (length: 4) -1123: Identifier("file2") at line 385, column 46 (length: 5) -1124: KeywordClose at line 386, column 1 (length: 5) -1125: KeywordFile at line 386, column 7 (length: 4) -1126: Identifier("file2") at line 386, column 12 (length: 5) -1127: KeywordOpen at line 389, column 1 (length: 4) -1128: KeywordFile at line 389, column 6 (length: 4) -1129: KeywordAt at line 389, column 11 (length: 2) -1130: StringLiteral("temp1.txt") at line 389, column 14 (length: 11) -1131: KeywordAnd at line 389, column 26 (length: 3) -1132: KeywordRead at line 389, column 30 (length: 4) -1133: KeywordContent at line 389, column 35 (length: 7) -1134: KeywordAs at line 389, column 43 (length: 2) -1135: Identifier("content1") at line 389, column 46 (length: 8) -1136: KeywordOpen at line 390, column 1 (length: 4) -1137: KeywordFile at line 390, column 6 (length: 4) -1138: KeywordAt at line 390, column 11 (length: 2) -1139: StringLiteral("temp2.txt") at line 390, column 14 (length: 11) -1140: KeywordAnd at line 390, column 26 (length: 3) -1141: KeywordRead at line 390, column 30 (length: 4) -1142: KeywordContent at line 390, column 35 (length: 7) -1143: KeywordAs at line 390, column 43 (length: 2) -1144: Identifier("content2") at line 390, column 46 (length: 8) -1145: KeywordStore at line 393, column 1 (length: 5) -1146: Identifier("concurrent_counter") at line 393, column 7 (length: 18) -1147: KeywordAs at line 393, column 26 (length: 2) -1148: IntLiteral(0) at line 393, column 29 (length: 1) -1149: KeywordCount at line 394, column 1 (length: 5) -1150: KeywordFrom at line 394, column 7 (length: 4) -1151: IntLiteral(1) at line 394, column 12 (length: 1) -1152: KeywordTo at line 394, column 14 (length: 2) -1153: IntLiteral(100) at line 394, column 17 (length: 3) -1154: Colon at line 394, column 20 (length: 1) -1155: KeywordChange at line 395, column 5 (length: 6) -1156: Identifier("concurrent_counter") at line 395, column 12 (length: 18) -1157: KeywordTo at line 395, column 31 (length: 2) -1158: Identifier("concurrent_counter") at line 395, column 34 (length: 18) -1159: KeywordPlus at line 395, column 53 (length: 4) -1160: IntLiteral(1) at line 395, column 58 (length: 1) -1161: KeywordEnd at line 396, column 1 (length: 3) -1162: KeywordCount at line 396, column 5 (length: 5) -1163: KeywordWait at line 399, column 1 (length: 4) -1164: KeywordFor at line 399, column 6 (length: 3) -1165: Identifier("content1") at line 399, column 10 (length: 8) -1166: KeywordWait at line 400, column 1 (length: 4) -1167: KeywordFor at line 400, column 6 (length: 3) -1168: Identifier("content2") at line 400, column 10 (length: 8) -1169: KeywordCheck at line 403, column 1 (length: 5) -1170: KeywordIf at line 403, column 7 (length: 2) -1171: Identifier("content1") at line 403, column 10 (length: 8) -1172: KeywordIs at line 403, column 19 (length: 2) -1173: KeywordEqual at line 403, column 22 (length: 5) -1174: KeywordTo at line 403, column 28 (length: 2) -1175: StringLiteral("FileOneContent") at line 403, column 31 (length: 16) -1176: KeywordAnd at line 403, column 48 (length: 3) -1177: Identifier("content2") at line 403, column 52 (length: 8) -1178: KeywordIs at line 403, column 61 (length: 2) -1179: KeywordEqual at line 403, column 64 (length: 5) -1180: KeywordTo at line 403, column 70 (length: 2) -1181: StringLiteral("FileTwoContent") at line 403, column 73 (length: 16) -1182: Colon at line 403, column 89 (length: 1) -1183: Identifier("log_message") at line 404, column 5 (length: 11) -1184: KeywordWith at line 404, column 17 (length: 4) -1185: StringLiteral("Concurrent file read test: PASS (content1 & content2 OK)") at line 404, column 22 (length: 58) -1186: KeywordOtherwise at line 405, column 1 (length: 9) -1187: Colon at line 405, column 10 (length: 1) -1188: Identifier("log_message") at line 406, column 5 (length: 11) -1189: KeywordWith at line 406, column 17 (length: 4) -1190: StringLiteral("Concurrent file read test: FAIL (content1=") at line 406, column 22 (length: 44) -1191: KeywordWith at line 406, column 67 (length: 4) -1192: Identifier("content1") at line 406, column 72 (length: 8) -1193: KeywordWith at line 406, column 81 (length: 4) -1194: StringLiteral(", content2=") at line 406, column 86 (length: 13) -1195: KeywordWith at line 406, column 100 (length: 4) -1196: Identifier("content2") at line 406, column 105 (length: 8) -1197: KeywordWith at line 406, column 114 (length: 4) -1198: StringLiteral(")") at line 406, column 119 (length: 3) -1199: KeywordEnd at line 407, column 1 (length: 3) -1200: KeywordCheck at line 407, column 5 (length: 5) -1201: Identifier("log_message") at line 409, column 1 (length: 11) -1202: KeywordWith at line 409, column 13 (length: 4) -1203: StringLiteral("Async I/O and Concurrency Tests completed.") at line 409, column 18 (length: 44) -1204: KeywordClose at line 416, column 1 (length: 5) -1205: KeywordFile at line 416, column 7 (length: 4) -1206: Identifier("logHandle log_message") at line 416, column 12 (length: 21) -1207: KeywordWith at line 418, column 13 (length: 4) -1208: StringLiteral("All tests completed.") at line 418, column 18 (length: 22) -1209: KeywordDisplay at line 419, column 1 (length: 7) -1210: StringLiteral("Nexus WFL Integration Testing finished. See nexus.log for details.") at line 419, column 9 (length: 68) diff --git a/Nexus/nexus_dev.wfl b/Nexus/nexus_dev.wfl deleted file mode 100644 index 6945b696..00000000 --- a/Nexus/nexus_dev.wfl +++ /dev/null @@ -1,282 +0,0 @@ -// Nexus WFL Integration Test Script -// This script ("nexus.wfl") performs integration tests of core WFL features. -// It logs progress and results to "nexus.log" for debugging. - -/////////////////////////////////////////////////////////////////////////// -// 1. Setup: Initialize logging -/////////////////////////////////////////////////////////////////////////// - -// Open the log file for writing -open file at "nexus.log" as logHandle - -// Create/truncate the log file initially with a proper line ending -wait for write content "=== Nexus WFL Integration Test Suite === -" into logHandle - -// Helper: Efficiently append a message line to the log file -define action called log_message needs message_text: - // Use append mode for efficient logging - add the message with line ending - wait for append content message_text with " -" into logHandle -end action - -// Log the start of the test suite -log_message with "Starting Nexus WFL Integration Test Suite..." - -/////////////////////////////////////////////////////////////////////////// -// 2. Variable Assignment & Arithmetic Tests -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Arithmetic Tests..." - -store a as 6 -store b as 2 - -// Test addition -store add_result as a plus b // 6 + 2 = 8 -check if add_result is equal to 8: - log_message with "Addition test: PASS" -otherwise: - log_message with "Addition test: FAIL (expected 8, got " with add_result with ")" -end check - -// Test subtraction -store sub_result as a minus b // 6 - 2 = 4 -check if sub_result is equal to 4: - log_message with "Subtraction test: PASS" -otherwise: - log_message with "Subtraction test: FAIL (expected 4, got " with sub_result with ")" -end check - -// Test multiplication -store mul_result as a times b // 6 * 2 = 12 -check if mul_result is equal to 12: - log_message with "Multiplication test: PASS" -otherwise: - log_message with "Multiplication test: FAIL (expected 12, got " with mul_result with ")" -end check - -// Test division (non-zero) -store div_result as a divided by b // 6 / 2 = 3 -check if div_result is equal to 3: - log_message with "Division test: PASS" -otherwise: - log_message with "Division test: FAIL (expected 3, got " with div_result with ")" -end check - -// Test floating-point division accuracy (5/2 = 2.5) -store x as 5 -store y as 2 -store frac_result as x divided by y // 5 / 2 = 2.5 -// Check by multiplying result by 2 to see if we get back 5 -store comparison_value as frac_result times 2 -check if comparison_value is equal to x: - log_message with "Fractional division test: PASS" -otherwise: - log_message with "Fractional division test: FAIL (expected 2.5, got " with frac_result with ")" -end check - -log_message with "Arithmetic Tests completed." - -/////////////////////////////////////////////////////////////////////////// -// 3. Control Flow (If/Else) Tests -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Control Flow (If/Else) Tests..." - -store m as 10 -store n as 5 - -// Test if-else (true condition) -check if m is greater than n: - store result1 as "yes" -otherwise: - store result1 as "no" -end check -check if result1 is equal to "yes": - log_message with "If condition TRUE branch test: PASS" -otherwise: - log_message with "If condition TRUE branch test: FAIL (expected yes, got " with result1 with ")" -end check - -// Test if-else (false condition) -check if m is less than n: - store result2 as "yes" -otherwise: - store result2 as "no" -end check -check if result2 is equal to "no": - log_message with "If condition FALSE branch test: PASS" -otherwise: - log_message with "If condition FALSE branch test: FAIL (expected no, got " with result2 with ")" -end check - -// Test if (no else branch) -store result3 as "no" -check if m is greater than n: - change result3 to "yes" -end check -check if result3 is equal to "yes": - log_message with "If (no else) true-case test: PASS" -otherwise: - log_message with "If (no else) true-case test: FAIL" -end check - -// Test single-line if/then/otherwise -store result4 as "yes" -if m is equal to n then change result4 to "yes" otherwise change result4 to "no" -check if result4 is equal to "no": - log_message with "Single-line if/then/otherwise test: PASS" -otherwise: - log_message with "Single-line if/then/otherwise test: FAIL (expected no, got " with result4 with ")" -end check - -log_message with "Control Flow (If/Else) Tests completed." - - -/////////////////////////////////////////////////////////////////////////// -// 4. Loop Tests (Count, For-Each, While, Repeat/Until, Forever, Break/Continue) -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Loop Tests..." - -// 4.1 Count Loop test -store sum_count as 0 -count from 1 to 5: - change sum_count to sum_count plus count -end count -// Expected sum_count = 1+2+3+4+5 = 15 -check if sum_count is equal to 15: - log_message with "Count loop test (1 to 5 sum): PASS" -otherwise: - log_message with "Count loop test (expected 15, got " with sum_count with "): FAIL" -end check - -// 4.2 For-Each Loop test -create list as numbers -push with numbers and 1 -push with numbers and 2 -push with numbers and 3 - -store sum_for_each as 0 -for each num in numbers: - change sum_for_each to sum_for_each plus num -end for - -// 4.3 While Loop test -store count1 as 1 -store sum_while as 0 -repeat while count1 is less than or equal to 5: - change sum_while to sum_while plus count1 - change count1 to count1 plus 1 -end repeat -// Expected sum_while = 15 (same as 1+...+5) -check if sum_while is equal to 15: - log_message with "While loop test (1 to 5 sum): PASS" -otherwise: - log_message with "While loop test (expected 15, got " with sum_while with "): FAIL" -end check - -// 4.4 Continue (Skip) in loop test – sum of odd numbers 1..5 -store count2 as 0 -store total_odds as 0 -repeat while count2 is less than 5: - change count2 to count2 plus 1 - // Skip even numbers - use a direct approach that we know works - check if count2 is equal to 2: - log_message with "Debug: Skipping even number " with count2 - skip // (continue to next iteration) - end check - - check if count2 is equal to 4: - log_message with "Debug: Skipping even number " with count2 - skip // (continue to next iteration) - end check - - log_message with "Debug: Adding odd number " with count2 with " to total_odds" - change total_odds to total_odds plus count2 -end repeat -// This loop adds only odd numbers 1+3+5 = 9 -check if total_odds is equal to 9: - log_message with "Loop continue/skip test (sum of odds 1-5): PASS" -otherwise: - log_message with "Loop continue/skip test (expected 9, got " with total_odds with "): FAIL" -end check - -// 4.5 Repeat-Until Loop test (do-while equivalent) -store count3 as 1 -store sum_repeat as 0 -repeat: - change sum_repeat to sum_repeat plus count3 - change count3 to count3 plus 1 -until count3 is greater than 5 -end repeat -// Loop executes until count3 > 5, so it runs for count3=1..5, sum_repeat = 15 -check if sum_repeat is equal to 15: - log_message with "Repeat-until loop test (1 to 5 sum): PASS" -otherwise: - log_message with "Repeat-until loop test (expected 15, got " with sum_repeat with "): FAIL" -end check - -// 4.6 Forever Loop test (infinite loop with break) -store k as 0 -repeat forever: - change k to k plus 1 - check if k is equal to 5: - break // break out of the forever loop when k == 5 - end check -end repeat -check if k is equal to 5: - log_message with "Forever loop with break test: PASS" -otherwise: - log_message with "Forever loop with break test: FAIL (k = " with k with ")" -end check - -// 4.7 Nested Loop Break vs Exit test -store break_outer_counter as 0 -count from 1 to 3: - count from 1 to 3: - check if count is equal to 2: - break // breaks inner loop only - end check - end count - change break_outer_counter to break_outer_counter plus 1 -end count -// After using 'break', outer loop should still complete all 3 iterations -check if break_outer_counter is equal to 3: - log_message with "Nested loop 'break' test: PASS" -otherwise: - log_message with "Nested loop 'break' test: FAIL (outer iterations = " with break_outer_counter with ")" -end check - -store exit_outer_counter as 0 -count from 1 to 3: - count from 1 to 3: - check if count is equal to 2: - exit loop // exit the outer loop entirely - end check - end count - // Only increment outer counter if loop wasn't exited - change exit_outer_counter to exit_outer_counter plus 1 -end count -// 'exit loop' should break out of the outer loop on the first iteration when inner count == 2 -check if exit_outer_counter is equal to 0: - log_message with "Nested loop 'exit' test: PASS" -otherwise: - log_message with "Nested loop 'exit' test: FAIL (outer iterations = " with exit_outer_counter with ")" -end check - -log_message with "Loop Tests completed." - -/////////////////////////////////////////////////////////////////////////// -// 5. Action (Function) Definition and Call Tests -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Action/Function Tests..." - -// Define actions to test various features - -// 5.1 No-parameter action (side effect) -define action called greet: - // Simply display a greeting (side-effect) - display "Hello, World from WFL!" -end action - -// Call the greet action -greet \ No newline at end of file diff --git a/Nexus/nexus_dev.wfl.lex.txt b/Nexus/nexus_dev.wfl.lex.txt deleted file mode 100644 index 037954c3..00000000 --- a/Nexus/nexus_dev.wfl.lex.txt +++ /dev/null @@ -1,201 +0,0 @@ -Lexer output for: nexus_dev.wfl -============================================== - - 0: KeywordOpen at line 10, column 1 (length: 4) - 1: KeywordFile at line 10, column 6 (length: 4) - 2: KeywordAt at line 10, column 11 (length: 2) - 3: StringLiteral("nexus.log") at line 10, column 14 (length: 11) - 4: KeywordAs at line 10, column 26 (length: 2) - 5: Identifier("logHandle") at line 10, column 29 (length: 9) - 6: KeywordWait at line 13, column 1 (length: 4) - 7: KeywordFor at line 13, column 6 (length: 3) - 8: KeywordWrite at line 13, column 10 (length: 5) - 9: KeywordContent at line 13, column 16 (length: 7) - 10: StringLiteral("=== Nexus WFL Integration Test Suite ===\n") at line 13, column 24 (length: 43) - 11: KeywordInto at line 14, column 3 (length: 4) - 12: Identifier("logHandle") at line 14, column 8 (length: 9) - 13: KeywordDefine at line 17, column 1 (length: 6) - 14: KeywordAction at line 17, column 8 (length: 6) - 15: KeywordCalled at line 17, column 15 (length: 6) - 16: Identifier("log_message") at line 17, column 22 (length: 11) - 17: KeywordNeeds at line 17, column 34 (length: 5) - 18: Identifier("message_text") at line 17, column 40 (length: 12) - 19: Colon at line 17, column 52 (length: 1) - 20: KeywordWait at line 19, column 5 (length: 4) - 21: KeywordFor at line 19, column 10 (length: 3) - 22: KeywordAppend at line 19, column 14 (length: 6) - 23: KeywordContent at line 19, column 21 (length: 7) - 24: Identifier("message_text") at line 19, column 29 (length: 12) - 25: KeywordWith at line 19, column 42 (length: 4) - 26: StringLiteral("\n") at line 19, column 47 (length: 3) - 27: KeywordInto at line 20, column 3 (length: 4) - 28: Identifier("logHandle") at line 20, column 8 (length: 9) - 29: KeywordEnd at line 21, column 1 (length: 3) - 30: KeywordAction at line 21, column 5 (length: 6) - 31: Identifier("log_message") at line 24, column 1 (length: 11) - 32: KeywordWith at line 24, column 13 (length: 4) - 33: StringLiteral("Starting Nexus WFL Integration Test Suite...") at line 24, column 18 (length: 46) - 34: Identifier("log_message") at line 29, column 1 (length: 11) - 35: KeywordWith at line 29, column 13 (length: 4) - 36: StringLiteral("Starting Arithmetic Tests...") at line 29, column 18 (length: 30) - 37: KeywordStore at line 31, column 1 (length: 5) - 38: Identifier("a") at line 31, column 7 (length: 1) - 39: KeywordAs at line 31, column 9 (length: 2) - 40: IntLiteral(6) at line 31, column 12 (length: 1) - 41: KeywordStore at line 32, column 1 (length: 5) - 42: Identifier("b") at line 32, column 7 (length: 1) - 43: KeywordAs at line 32, column 9 (length: 2) - 44: IntLiteral(2) at line 32, column 12 (length: 1) - 45: KeywordStore at line 35, column 1 (length: 5) - 46: Identifier("add_result") at line 35, column 7 (length: 10) - 47: KeywordAs at line 35, column 18 (length: 2) - 48: Identifier("a") at line 35, column 21 (length: 1) - 49: KeywordPlus at line 35, column 23 (length: 4) - 50: Identifier("b") at line 35, column 28 (length: 1) - 51: KeywordCheck at line 36, column 1 (length: 5) - 52: KeywordIf at line 36, column 7 (length: 2) - 53: Identifier("add_result") at line 36, column 10 (length: 10) - 54: KeywordIs at line 36, column 21 (length: 2) - 55: KeywordEqual at line 36, column 24 (length: 5) - 56: KeywordTo at line 36, column 30 (length: 2) - 57: IntLiteral(8) at line 36, column 33 (length: 1) - 58: Colon at line 36, column 34 (length: 1) - 59: Identifier("log_message") at line 37, column 5 (length: 11) - 60: KeywordWith at line 37, column 17 (length: 4) - 61: StringLiteral("Addition test: PASS") at line 37, column 22 (length: 21) - 62: KeywordOtherwise at line 38, column 1 (length: 9) - 63: Colon at line 38, column 10 (length: 1) - 64: Identifier("log_message") at line 39, column 5 (length: 11) - 65: KeywordWith at line 39, column 17 (length: 4) - 66: StringLiteral("Addition test: FAIL (expected 8, got ") at line 39, column 22 (length: 39) - 67: KeywordWith at line 39, column 62 (length: 4) - 68: Identifier("add_result") at line 39, column 67 (length: 10) - 69: KeywordWith at line 39, column 78 (length: 4) - 70: StringLiteral(")") at line 39, column 83 (length: 3) - 71: KeywordEnd at line 40, column 1 (length: 3) - 72: KeywordCheck at line 40, column 5 (length: 5) - 73: KeywordStore at line 43, column 1 (length: 5) - 74: Identifier("sub_result") at line 43, column 7 (length: 10) - 75: KeywordAs at line 43, column 18 (length: 2) - 76: Identifier("a") at line 43, column 21 (length: 1) - 77: KeywordMinus at line 43, column 23 (length: 5) - 78: Identifier("b") at line 43, column 29 (length: 1) - 79: KeywordCheck at line 44, column 1 (length: 5) - 80: KeywordIf at line 44, column 7 (length: 2) - 81: Identifier("sub_result") at line 44, column 10 (length: 10) - 82: KeywordIs at line 44, column 21 (length: 2) - 83: KeywordEqual at line 44, column 24 (length: 5) - 84: KeywordTo at line 44, column 30 (length: 2) - 85: IntLiteral(4) at line 44, column 33 (length: 1) - 86: Colon at line 44, column 34 (length: 1) - 87: Identifier("log_message") at line 45, column 5 (length: 11) - 88: KeywordWith at line 45, column 17 (length: 4) - 89: StringLiteral("Subtraction test: PASS") at line 45, column 22 (length: 24) - 90: KeywordOtherwise at line 46, column 1 (length: 9) - 91: Colon at line 46, column 10 (length: 1) - 92: Identifier("log_message") at line 47, column 5 (length: 11) - 93: KeywordWith at line 47, column 17 (length: 4) - 94: StringLiteral("Subtraction test: FAIL (expected 4, got ") at line 47, column 22 (length: 42) - 95: KeywordWith at line 47, column 65 (length: 4) - 96: Identifier("sub_result") at line 47, column 70 (length: 10) - 97: KeywordWith at line 47, column 81 (length: 4) - 98: StringLiteral(")") at line 47, column 86 (length: 3) - 99: KeywordEnd at line 48, column 1 (length: 3) - 100: KeywordCheck at line 48, column 5 (length: 5) - 101: KeywordStore at line 51, column 1 (length: 5) - 102: Identifier("mul_result") at line 51, column 7 (length: 10) - 103: KeywordAs at line 51, column 18 (length: 2) - 104: Identifier("a") at line 51, column 21 (length: 1) - 105: KeywordTimes at line 51, column 23 (length: 5) - 106: Identifier("b") at line 51, column 29 (length: 1) - 107: KeywordCheck at line 52, column 1 (length: 5) - 108: KeywordIf at line 52, column 7 (length: 2) - 109: Identifier("mul_result") at line 52, column 10 (length: 10) - 110: KeywordIs at line 52, column 21 (length: 2) - 111: KeywordEqual at line 52, column 24 (length: 5) - 112: KeywordTo at line 52, column 30 (length: 2) - 113: IntLiteral(12) at line 52, column 33 (length: 2) - 114: Colon at line 52, column 35 (length: 1) - 115: Identifier("log_message") at line 53, column 5 (length: 11) - 116: KeywordWith at line 53, column 17 (length: 4) - 117: StringLiteral("Multiplication test: PASS") at line 53, column 22 (length: 27) - 118: KeywordOtherwise at line 54, column 1 (length: 9) - 119: Colon at line 54, column 10 (length: 1) - 120: Identifier("log_message") at line 55, column 5 (length: 11) - 121: KeywordWith at line 55, column 17 (length: 4) - 122: StringLiteral("Multiplication test: FAIL (expected 12, got ") at line 55, column 22 (length: 46) - 123: KeywordWith at line 55, column 69 (length: 4) - 124: Identifier("mul_result") at line 55, column 74 (length: 10) - 125: KeywordWith at line 55, column 85 (length: 4) - 126: StringLiteral(")") at line 55, column 90 (length: 3) - 127: KeywordEnd at line 56, column 1 (length: 3) - 128: KeywordCheck at line 56, column 5 (length: 5) - 129: KeywordStore at line 59, column 1 (length: 5) - 130: Identifier("div_result") at line 59, column 7 (length: 10) - 131: KeywordAs at line 59, column 18 (length: 2) - 132: Identifier("a") at line 59, column 21 (length: 1) - 133: KeywordDividedBy at line 59, column 23 (length: 10) - 134: Identifier("b") at line 59, column 34 (length: 1) - 135: KeywordCheck at line 60, column 1 (length: 5) - 136: KeywordIf at line 60, column 7 (length: 2) - 137: Identifier("div_result") at line 60, column 10 (length: 10) - 138: KeywordIs at line 60, column 21 (length: 2) - 139: KeywordEqual at line 60, column 24 (length: 5) - 140: KeywordTo at line 60, column 30 (length: 2) - 141: IntLiteral(3) at line 60, column 33 (length: 1) - 142: Colon at line 60, column 34 (length: 1) - 143: Identifier("log_message") at line 61, column 5 (length: 11) - 144: KeywordWith at line 61, column 17 (length: 4) - 145: StringLiteral("Division test: PASS") at line 61, column 22 (length: 21) - 146: KeywordOtherwise at line 62, column 1 (length: 9) - 147: Colon at line 62, column 10 (length: 1) - 148: Identifier("log_message") at line 63, column 5 (length: 11) - 149: KeywordWith at line 63, column 17 (length: 4) - 150: StringLiteral("Division test: FAIL (expected 3, got ") at line 63, column 22 (length: 39) - 151: KeywordWith at line 63, column 62 (length: 4) - 152: Identifier("div_result") at line 63, column 67 (length: 10) - 153: KeywordWith at line 63, column 78 (length: 4) - 154: StringLiteral(")") at line 63, column 83 (length: 3) - 155: KeywordEnd at line 64, column 1 (length: 3) - 156: KeywordCheck at line 64, column 5 (length: 5) - 157: KeywordStore at line 67, column 1 (length: 5) - 158: Identifier("x") at line 67, column 7 (length: 1) - 159: KeywordAs at line 67, column 9 (length: 2) - 160: IntLiteral(5) at line 67, column 12 (length: 1) - 161: KeywordStore at line 68, column 1 (length: 5) - 162: Identifier("y") at line 68, column 7 (length: 1) - 163: KeywordAs at line 68, column 9 (length: 2) - 164: IntLiteral(2) at line 68, column 12 (length: 1) - 165: KeywordStore at line 69, column 1 (length: 5) - 166: Identifier("frac_result") at line 69, column 7 (length: 11) - 167: KeywordAs at line 69, column 19 (length: 2) - 168: Identifier("x") at line 69, column 22 (length: 1) - 169: KeywordDividedBy at line 69, column 24 (length: 10) - 170: Identifier("y") at line 69, column 35 (length: 1) - 171: KeywordCheck at line 71, column 1 (length: 5) - 172: KeywordIf at line 71, column 7 (length: 2) - 173: Identifier("frac_result") at line 71, column 10 (length: 11) - 174: KeywordTimes at line 71, column 22 (length: 5) - 175: IntLiteral(2) at line 71, column 28 (length: 1) - 176: KeywordIs at line 71, column 30 (length: 2) - 177: KeywordEqual at line 71, column 33 (length: 5) - 178: KeywordTo at line 71, column 39 (length: 2) - 179: Identifier("x") at line 71, column 42 (length: 1) - 180: Colon at line 71, column 43 (length: 1) - 181: Identifier("log_message") at line 72, column 5 (length: 11) - 182: KeywordWith at line 72, column 17 (length: 4) - 183: StringLiteral("Fractional division test: PASS") at line 72, column 22 (length: 32) - 184: KeywordOtherwise at line 73, column 1 (length: 9) - 185: Colon at line 73, column 10 (length: 1) - 186: Identifier("log_message") at line 74, column 5 (length: 11) - 187: KeywordWith at line 74, column 17 (length: 4) - 188: StringLiteral("Fractional division test: FAIL (expected 2.5, got ") at line 74, column 22 (length: 52) - 189: KeywordWith at line 74, column 75 (length: 4) - 190: Identifier("frac_result") at line 74, column 80 (length: 11) - 191: KeywordWith at line 74, column 92 (length: 4) - 192: StringLiteral(")") at line 74, column 97 (length: 3) - 193: KeywordEnd at line 75, column 1 (length: 3) - 194: KeywordCheck at line 75, column 5 (length: 5) - 195: Identifier("log_message") at line 77, column 1 (length: 11) - 196: KeywordWith at line 77, column 13 (length: 4) - 197: StringLiteral("Arithmetic Tests completed.") at line 77, column 18 (length: 29) diff --git a/Nexus/nexus_dev_debug.txt b/Nexus/nexus_dev_debug.txt deleted file mode 100644 index b0affdc7..00000000 --- a/Nexus/nexus_dev_debug.txt +++ /dev/null @@ -1,19 +0,0 @@ -=== WFL Debug Report === -Script: .\Nexus\nexus_dev.wfl -Time: 2025-05-27 10:31:43 - -=== Error Summary === -Runtime error at line 154, column 18: Undefined variable 'empty list' - -=== Stack Trace === -In main script at line 154, column 18 - -=== Source Code === - 152: // 4.2 For-Each Loop test - 153: // Explicitly create an empty list to ensure proper type recognition ->> 154: store numbers as empty list - 155: push with numbers and 1 - 156: push with numbers and 2 - -=== Local Variables === -(No local variables in global scope) diff --git a/Nexus/nexus_minimal.wfl b/Nexus/nexus_minimal.wfl deleted file mode 100644 index b78b7a49..00000000 --- a/Nexus/nexus_minimal.wfl +++ /dev/null @@ -1,127 +0,0 @@ -// Minimal Nexus WFL Integration Test Script -// This is a reduced version of nexus.wfl that avoids timeout issues - -/////////////////////////////////////////////////////////////////////////// -// 1. Setup: Initialize logging -/////////////////////////////////////////////////////////////////////////// - -// Open the log file (will be truncated/created anew) -open file at "nexus_minimal.log" as logHandle - -// Helper: Append a message line to the log file -define action called log_message needs message: - // Write message to log file - wait for write content message with "\n" into logHandle -end action - -// Log the start of the test suite -log_message with "Starting Minimal Nexus WFL Integration Test Suite..." - -/////////////////////////////////////////////////////////////////////////// -// 2. Variable Assignment & Arithmetic Tests -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Arithmetic Tests..." - -store a as 6 -store b as 2 - -// Test addition -store add_result as a plus b // 6 + 2 = 8 -check if add_result is equal to 8: - log_message with "Addition test: PASS" -otherwise: - log_message with "Addition test: FAIL (expected 8, got " with add_result with ")" -end check - -// Test subtraction -store sub_result as a minus b // 6 - 2 = 4 -check if sub_result is equal to 4: - log_message with "Subtraction test: PASS" -otherwise: - log_message with "Subtraction test: FAIL (expected 4, got " with sub_result with ")" -end check - -/////////////////////////////////////////////////////////////////////////// -// 3. Comparison Tests -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Comparison Tests..." - -// Test greater than -store m as 5 -store n as 10 -check if n is greater than m: - log_message with "Greater than test: PASS" -otherwise: - log_message with "Greater than test: FAIL" -end check - -// Test less than -check if m is less than n: - log_message with "Less than test: PASS" -otherwise: - log_message with "Less than test: FAIL" -end check - -/////////////////////////////////////////////////////////////////////////// -// 4. Loop Tests (Limited) -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Loop Tests..." - -// Count Loop test -store sum_count as 0 -count from 1 to 5: - change sum_count to sum_count plus count -end count -// Expected sum_count = 1+2+3+4+5 = 15 -check if sum_count is equal to 15: - log_message with "Count loop test: PASS" -otherwise: - log_message with "Count loop test: FAIL (expected 15, got " with sum_count with ")" -end check - -/////////////////////////////////////////////////////////////////////////// -// 5. Action Tests (Limited) -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Action Tests..." - -// Single-parameter action with return -define action called square needs value: - give back value times value -end action - -// Test square action -store sq_result as square with 4 // 4^2 = 16 -check if sq_result is equal to 16: - log_message with "Action test (square): PASS" -otherwise: - log_message with "Action test (square): FAIL (expected 16, got " with sq_result with ")" -end check - -/////////////////////////////////////////////////////////////////////////// -// 6. File Operations Test -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting File Operations Test..." - -// Create a test file -open file at "test_file.txt" as testFile -wait for write content "Test content" into testFile -close file testFile - -// Read the file content -wait for open file at "test_file.txt" and read content as fileContent -check if fileContent is equal to "Test content": - log_message with "File read test: PASS" -otherwise: - log_message with "File read test: FAIL (expected 'Test content', got '" with fileContent with "')" -end check - -/////////////////////////////////////////////////////////////////////////// -// End of tests -/////////////////////////////////////////////////////////////////////////// - -// Final message -log_message with "All tests completed." -display "Minimal Nexus WFL Integration Testing finished. See nexus_minimal.log for details." - -// Close the log file -close file logHandle diff --git a/Nexus/test.hold b/Nexus/test.hold deleted file mode 100644 index 275590a0..00000000 --- a/Nexus/test.hold +++ /dev/null @@ -1,121 +0,0 @@ -// --------------------------------------------------------------------- -// 5.0 Logging helper (using display, as per wfl-actions.md for output) -// --------------------------------------------------------------------- -define action log_message with message_text: - display "LOG: " with message_text -end action - -// --------------------------------------------------------------------- -// 5.A Tiny assertion helpers -// --------------------------------------------------------------------- -define action assert_equal with label and expected and actual: - if expected is equal to actual: - log_message with label with ": PASS" - else: - log_message with label with ": FAIL (expected " with expected with ", got " with actual with ")" - end if -end action - -define action assert_throws with label and the_action: - try: - the_action // Invoke the action directly by its parameter name - log_message with label with ": FAIL (no error thrown)" - when error: // Assuming "error" is a generic catch, as per original test intent - log_message with label with ": PASS (caught '" with error message with "')" // Using 'error message' variable - end try -end action - -/////////////////////////////////////////////////////////////////////////// -// 5. Action (Function) Definition and Call Tests -/////////////////////////////////////////////////////////////////////////// -log_message with "Starting Action/Function Tests..." - -// Define actions to test various features - -// 5.1 No-parameter action (side effect) -define action greet_test_action: - // Simply display a greeting (side-effect) - display "Hello, World from WFL greet_test_action!" -end action - -// 5.2 Single-parameter action with return -define action square_test_action with value: - provide value times value -end action - -// 5.3 Multi-parameter action with return -define action add_test_action with p and q: - provide p plus q -end action - -// 5.4 Recursive action (factorial) -define action factorial_test_action with n: - if n is equal to 0: - provide 1 - else: - // Recursive call - store n_minus_1 as n minus 1 - store recursive_result as factorial_test_action with n_minus_1 - provide n times recursive_result - end if -end action - -// 5.5 Action that triggers an error (for error handling test) -define action faulty_test_action: - // This action will cause a division by zero error - store u as 1 - store v as 0 - store w as u divided by v // runtime error (division by zero) - provide w -end action - -// Call actions and verify results using assert helpers - -// Test greet_test_action (no return value, just side effect) -log_message with "Test: greet_test_action execution" -greet_test_action // Direct call - -// Test square_test_action -store sq_input as 4 -store sq_expected as 16 -store sq_actual as square_test_action with sq_input // Direct call, store result -assert_equal with "Action test (square 4 -> 16)" and sq_expected and sq_actual - -store sq_input_neg as -5 -store sq_expected_neg as 25 -store sq_actual_neg as square_test_action with sq_input_neg -assert_equal with "Action test (square -5 -> 25)" and sq_expected_neg and sq_actual_neg - -// Test add_test_action -store add_input_p1 as 10 -store add_input_q1 as 15 -store add_expected1 as 25 -store add_actual1 as add_test_action with add_input_p1 and add_input_q1 -assert_equal with "Action test (add 10+15 -> 25)" and add_expected1 and add_actual1 - -store add_input_p2 as -5 -store add_input_q2 as 3 -store add_expected2 as -2 -store add_actual2 as add_test_action with add_input_p2 and add_input_q2 -assert_equal with "Action test (add -5+3 -> -2)" and add_expected2 and add_actual2 - -// Test recursive factorial_test_action -store fact_input1 as 5 -store fact_expected1 as 120 -store fact_actual1 as factorial_test_action with fact_input1 -assert_equal with "Action test (factorial 5 -> 120)" and fact_expected1 and fact_actual1 - -store fact_input2 as 0 -store fact_expected2 as 1 -store fact_actual2 as factorial_test_action with fact_input2 -assert_equal with "Action test (factorial 0 -> 1)" and fact_expected2 and fact_actual2 - -store fact_input3 as 1 -store fact_expected3 as 1 -store fact_actual3 as factorial_test_action with fact_input3 -assert_equal with "Action test (factorial 1 -> 1)" and fact_expected3 and fact_actual3 // Changed 'perform assert_equal' to direct call - -// Test error handling with faulty_test_action -assert_throws with "Action test (faulty_test_action)" and faulty_test_action // Changed 'perform assert_throws' - -log_message with "Action/Function Tests completed." \ No newline at end of file diff --git a/Nexus/test.wfl b/Nexus/test.wfl deleted file mode 100644 index b9cd219a..00000000 --- a/Nexus/test.wfl +++ /dev/null @@ -1,140 +0,0 @@ -// 5. Action (Function) Definition and Call Tests - with file logging - -// 5.0 Logging helper (MUST come first) -// Open the log file (will be truncated/created anew) -open file at "nexus.log" as logHandle - -// Helper: Append a message line to the log file (read current content, add message, write back) -define action called log_message needs message_text: - // Read current log content - wait for open file at "nexus.log" and read content as currentLog - // Append new message with proper newline to current content - store updatedLog as currentLog with message_text with " -" - // Write updated content back to log file - wait for write content updatedLog into logHandle -end action - -// Log the start of the test suite -log_message with "Starting Nexus WFL Integration Test Suite..." - -// 5.A Tiny assertion helpers -define action called assert_equal needs label and expected and actual: - check if expected is equal to actual: - store passMsg as label with ": PASS" - log_message with passMsg - otherwise: - store failMsg as label with ": FAIL (expected " with expected with ", got " with actual with ")" - log_message with failMsg - end check -end action - -define action called assert_throws needs label and the_action: - try: - the_action - log_message with label with ": FAIL (no error thrown)" - when error: - log_message with label with ": PASS (caught " with error with ")" - end try -end action - -// 5. Action (Function) Definition and Call Tests -log_message with "Starting Action/Function Tests..." - -// Define actions to test various features - -// 5.1 No-parameter action (side effect) -define action called greet_test_action: - // Simply display a greeting (side-effect) - // For testing, we can't easily assert console output here, - // but we can ensure it runs without error. - // If it needed to be asserted, it would write to a test-specific log or variable. - display "Hello, World from WFL greet_test_action!" -end action - - -// 5.2 Single-parameter action with return -define action called square_test_action needs value: - give back value times value -end action - -// 5.3 Multi-parameter action with return -define action called add_test_action needs p and q: - give back p plus q -end action - -// 5.4 Non-recursive action (simple multiplication for now) -define action called factorial_test_action needs n: - check if n is equal to 0: - give back 1 - otherwise: - check if n is equal to 1: - give back 1 - otherwise: - check if n is equal to 5: - give back 120 - otherwise: - give back 1 - end check - end check - end check -end action - -// 5.5 Action that triggers an error (for error handling test) -define action called faulty_test_action: - // This action will cause a division by zero error - store result as 1 divided by 0 // runtime error (division by zero) - give back result -end action - -// Call actions and verify results using assert helpers - -// Test greet_test_action (no return value, just side effect) -log_message with "Test: greet_test_action execution" -greet_test_action // Direct call - -// Test square_test_action -store sq_input as 4 -store sq_expected as 16 -store sq_actual as square_test_action with sq_input // Direct call, store result -assert_equal with "Action test (square 4 equals 16)" and sq_expected and sq_actual - -store sq_input_neg as 0 minus 5 -store sq_expected_neg as 25 -store sq_actual_neg as square_test_action with sq_input_neg -assert_equal with "Action test (square negative 5 equals 25)" and sq_expected_neg and sq_actual_neg - -// Test add_test_action -store add_input_p1 as 10 -store add_input_q1 as 15 -store add_expected1 as 25 -store add_actual1 as add_test_action with add_input_p1 and add_input_q1 -assert_equal with "Action test (add 10+15 equals 25)" and add_expected1 and add_actual1 - -store add_input_p2 as 0 minus 5 -store add_input_q2 as 3 -store add_expected2 as 0 minus 2 -store add_actual2 as add_test_action with add_input_p2 and add_input_q2 -assert_equal with "Action test (add negative 5 plus 3 equals negative 2)" and add_expected2 and add_actual2 - -// Test recursive factorial_test_action -store fact_input1 as 5 -store fact_expected1 as 120 -store fact_actual1 as factorial_test_action with fact_input1 -assert_equal with "Action test (factorial 5 equals 120)" and fact_expected1 and fact_actual1 - -store fact_input2 as 0 -store fact_expected2 as 1 -store fact_actual2 as factorial_test_action with fact_input2 -assert_equal with "Action test (factorial 0 equals 1)" and fact_expected2 and fact_actual2 - -store fact_input3 as 1 -store fact_expected3 as 1 -store fact_actual3 as factorial_test_action with fact_input3 -assert_equal with "Action test (factorial 1 equals 1)" and fact_expected3 and fact_actual3 - -// Test error handling with faulty_test_action -assert_throws with "Action test (faulty_test_action)" and faulty_test_action - -log_message with "Action/Function Tests completed." - diff --git a/Nexus/test.wfl.ast.txt b/Nexus/test.wfl.ast.txt deleted file mode 100644 index 5bfb1f99..00000000 --- a/Nexus/test.wfl.ast.txt +++ /dev/null @@ -1,377 +0,0 @@ -AST output for: ./Nexus/test.wfl -============================================== - -Program with 5 statements: - -Statement #1: OpenFileStatement { - path: Literal( - String( - "nexus.log", - ), - 9, - 14, - ), - variable_name: "logHandle", - line: 9, - column: 1, -} - -Statement #2: ActionDefinition { - name: "log_message", - parameters: [ - Parameter { - name: "message_text", - param_type: None, - default_value: None, - }, - ], - body: [ - WaitForStatement { - inner: ReadFileStatement { - path: Literal( - String( - "nexus.log", - ), - 14, - 27, - ), - variable_name: "currentLog", - line: 14, - column: 14, - }, - line: 14, - column: 5, - }, - VariableDeclaration { - name: "updatedLog", - value: Concatenation { - left: Variable( - "currentLog", - 16, - 25, - ), - right: Concatenation { - left: Variable( - "message_text", - 16, - 41, - ), - right: Literal( - String( - "\\n", - ), - 16, - 59, - ), - line: 16, - column: 54, - }, - line: 16, - column: 36, - }, - line: 16, - column: 5, - }, - WaitForStatement { - inner: WriteFileStatement { - file: Variable( - "logHandle", - 18, - 44, - ), - content: Variable( - "updatedLog", - 18, - 28, - ), - mode: Overwrite, - line: 18, - column: 5, - }, - line: 18, - column: 5, - }, - ], - return_type: None, - line: 22, - column: 1, -} - -Statement #3: ExpressionStatement { - expression: ActionCall { - name: "log_message", - arguments: [ - Argument { - name: None, - value: Literal( - String( - "Starting Nexus WFL Integration Test Suite...", - ), - 22, - 18, - ), - }, - ], - line: 22, - column: 1, - }, - line: 27, - column: 1, -} - -Statement #4: ActionDefinition { - name: "assert_equal", - parameters: [ - Parameter { - name: "label expected actual", - param_type: None, - default_value: None, - }, - ], - body: [ - IfStatement { - condition: BinaryOperation { - left: Variable( - "expected", - 28, - 14, - ), - operator: Equals, - right: Variable( - "actual", - 28, - 35, - ), - line: 28, - column: 23, - }, - then_block: [ - ExpressionStatement { - expression: ActionCall { - name: "log_message", - arguments: [ - Argument { - name: None, - value: Concatenation { - left: Variable( - "label", - 29, - 26, - ), - right: Literal( - String( - ": PASS", - ), - 29, - 37, - ), - line: 29, - column: 32, - }, - }, - ], - line: 29, - column: 9, - }, - line: 30, - column: 5, - }, - ], - else_block: Some( - [ - ExpressionStatement { - expression: ActionCall { - name: "log_message", - arguments: [ - Argument { - name: None, - value: Concatenation { - left: Variable( - "label", - 31, - 26, - ), - right: Concatenation { - left: Literal( - String( - ": FAIL (expected ", - ), - 31, - 37, - ), - right: Concatenation { - left: Variable( - "expected", - 31, - 62, - ), - right: Concatenation { - left: Literal( - String( - ", got ", - ), - 31, - 76, - ), - right: Concatenation { - left: Variable( - "actual", - 31, - 90, - ), - right: Literal( - String( - ")", - ), - 31, - 102, - ), - line: 31, - column: 97, - }, - line: 31, - column: 85, - }, - line: 31, - column: 71, - }, - line: 31, - column: 57, - }, - line: 31, - column: 32, - }, - }, - ], - line: 31, - column: 9, - }, - line: 32, - column: 5, - }, - ], - ), - line: 28, - column: 5, - }, - ], - return_type: None, - line: 35, - column: 1, -} - -Statement #5: ActionDefinition { - name: "assert_throws", - parameters: [ - Parameter { - name: "label the_action", - param_type: None, - default_value: None, - }, - ], - body: [ - TryStatement { - body: [ - ExpressionStatement { - expression: Variable( - "the_action", - 37, - 9, - ), - line: 38, - column: 9, - }, - ExpressionStatement { - expression: ActionCall { - name: "log_message", - arguments: [ - Argument { - name: None, - value: Concatenation { - left: Variable( - "label", - 38, - 26, - ), - right: Literal( - String( - ": FAIL (no error thrown)", - ), - 38, - 37, - ), - line: 38, - column: 32, - }, - }, - ], - line: 38, - column: 9, - }, - line: 39, - column: 5, - }, - ], - error_name: "error", - when_block: [ - ExpressionStatement { - expression: ActionCall { - name: "log_message", - arguments: [ - Argument { - name: None, - value: Concatenation { - left: Variable( - "label", - 40, - 26, - ), - right: Concatenation { - left: Literal( - String( - ": PASS (caught ", - ), - 40, - 37, - ), - right: Concatenation { - left: Variable( - "error", - 40, - 60, - ), - right: Literal( - String( - ")", - ), - 40, - 71, - ), - line: 40, - column: 66, - }, - line: 40, - column: 55, - }, - line: 40, - column: 32, - }, - }, - ], - line: 40, - column: 9, - }, - line: 41, - column: 5, - }, - ], - otherwise_block: None, - line: 36, - column: 5, - }, - ], - return_type: None, - line: 0, - column: 0, -} - diff --git a/Nexus/test.wfl.lex.txt b/Nexus/test.wfl.lex.txt deleted file mode 100644 index ae03ef6a..00000000 --- a/Nexus/test.wfl.lex.txt +++ /dev/null @@ -1,117 +0,0 @@ -Lexer output for: ./Nexus/test.wfl -============================================== - - 0: KeywordOpen at line 9, column 1 (length: 4) - 1: KeywordFile at line 9, column 6 (length: 4) - 2: KeywordAt at line 9, column 11 (length: 2) - 3: StringLiteral("nexus.log") at line 9, column 14 (length: 11) - 4: KeywordAs at line 9, column 26 (length: 2) - 5: Identifier("logHandle") at line 9, column 29 (length: 9) - 6: KeywordDefine at line 12, column 1 (length: 6) - 7: KeywordAction at line 12, column 8 (length: 6) - 8: KeywordCalled at line 12, column 15 (length: 6) - 9: Identifier("log_message") at line 12, column 22 (length: 11) - 10: KeywordNeeds at line 12, column 34 (length: 5) - 11: Identifier("message_text") at line 12, column 40 (length: 12) - 12: Colon at line 12, column 52 (length: 1) - 13: KeywordWait at line 14, column 5 (length: 4) - 14: KeywordFor at line 14, column 10 (length: 3) - 15: KeywordOpen at line 14, column 14 (length: 4) - 16: KeywordFile at line 14, column 19 (length: 4) - 17: KeywordAt at line 14, column 24 (length: 2) - 18: StringLiteral("nexus.log") at line 14, column 27 (length: 11) - 19: KeywordAnd at line 14, column 39 (length: 3) - 20: KeywordRead at line 14, column 43 (length: 4) - 21: KeywordContent at line 14, column 48 (length: 7) - 22: KeywordAs at line 14, column 56 (length: 2) - 23: Identifier("currentLog") at line 14, column 59 (length: 10) - 24: KeywordStore at line 16, column 5 (length: 5) - 25: Identifier("updatedLog") at line 16, column 11 (length: 10) - 26: KeywordAs at line 16, column 22 (length: 2) - 27: Identifier("currentLog") at line 16, column 25 (length: 10) - 28: KeywordWith at line 16, column 36 (length: 4) - 29: Identifier("message_text") at line 16, column 41 (length: 12) - 30: KeywordWith at line 16, column 54 (length: 4) - 31: StringLiteral("\\n") at line 16, column 59 (length: 4) - 32: KeywordWait at line 18, column 5 (length: 4) - 33: KeywordFor at line 18, column 10 (length: 3) - 34: KeywordWrite at line 18, column 14 (length: 5) - 35: KeywordContent at line 18, column 20 (length: 7) - 36: Identifier("updatedLog") at line 18, column 28 (length: 10) - 37: KeywordInto at line 18, column 39 (length: 4) - 38: Identifier("logHandle") at line 18, column 44 (length: 9) - 39: KeywordEnd at line 19, column 1 (length: 3) - 40: KeywordAction at line 19, column 5 (length: 6) - 41: Identifier("log_message") at line 22, column 1 (length: 11) - 42: KeywordWith at line 22, column 13 (length: 4) - 43: StringLiteral("Starting Nexus WFL Integration Test Suite...") at line 22, column 18 (length: 46) - 44: KeywordDefine at line 27, column 1 (length: 6) - 45: KeywordAction at line 27, column 8 (length: 6) - 46: KeywordCalled at line 27, column 15 (length: 6) - 47: Identifier("assert_equal") at line 27, column 22 (length: 12) - 48: KeywordNeeds at line 27, column 35 (length: 5) - 49: Identifier("label expected actual") at line 27, column 41 (length: 21) - 50: Colon at line 27, column 62 (length: 1) - 51: KeywordCheck at line 28, column 5 (length: 5) - 52: KeywordIf at line 28, column 11 (length: 2) - 53: Identifier("expected") at line 28, column 14 (length: 8) - 54: KeywordIs at line 28, column 23 (length: 2) - 55: KeywordEqual at line 28, column 26 (length: 5) - 56: KeywordTo at line 28, column 32 (length: 2) - 57: Identifier("actual") at line 28, column 35 (length: 6) - 58: Colon at line 28, column 41 (length: 1) - 59: Identifier("log_message") at line 29, column 9 (length: 11) - 60: KeywordWith at line 29, column 21 (length: 4) - 61: Identifier("label") at line 29, column 26 (length: 5) - 62: KeywordWith at line 29, column 32 (length: 4) - 63: StringLiteral(": PASS") at line 29, column 37 (length: 8) - 64: KeywordOtherwise at line 30, column 5 (length: 9) - 65: Colon at line 30, column 14 (length: 1) - 66: Identifier("log_message") at line 31, column 9 (length: 11) - 67: KeywordWith at line 31, column 21 (length: 4) - 68: Identifier("label") at line 31, column 26 (length: 5) - 69: KeywordWith at line 31, column 32 (length: 4) - 70: StringLiteral(": FAIL (expected ") at line 31, column 37 (length: 19) - 71: KeywordWith at line 31, column 57 (length: 4) - 72: Identifier("expected") at line 31, column 62 (length: 8) - 73: KeywordWith at line 31, column 71 (length: 4) - 74: StringLiteral(", got ") at line 31, column 76 (length: 8) - 75: KeywordWith at line 31, column 85 (length: 4) - 76: Identifier("actual") at line 31, column 90 (length: 6) - 77: KeywordWith at line 31, column 97 (length: 4) - 78: StringLiteral(")") at line 31, column 102 (length: 3) - 79: KeywordEnd at line 32, column 5 (length: 3) - 80: KeywordCheck at line 32, column 9 (length: 5) - 81: KeywordEnd at line 33, column 1 (length: 3) - 82: KeywordAction at line 33, column 5 (length: 6) - 83: KeywordDefine at line 35, column 1 (length: 6) - 84: KeywordAction at line 35, column 8 (length: 6) - 85: KeywordCalled at line 35, column 15 (length: 6) - 86: Identifier("assert_throws") at line 35, column 22 (length: 13) - 87: KeywordNeeds at line 35, column 36 (length: 5) - 88: Identifier("label the_action") at line 35, column 42 (length: 16) - 89: Colon at line 35, column 58 (length: 1) - 90: KeywordTry at line 36, column 5 (length: 3) - 91: Colon at line 36, column 8 (length: 1) - 92: Identifier("the_action") at line 37, column 9 (length: 10) - 93: Identifier("log_message") at line 38, column 9 (length: 11) - 94: KeywordWith at line 38, column 21 (length: 4) - 95: Identifier("label") at line 38, column 26 (length: 5) - 96: KeywordWith at line 38, column 32 (length: 4) - 97: StringLiteral(": FAIL (no error thrown)") at line 38, column 37 (length: 26) - 98: KeywordWhen at line 39, column 5 (length: 4) - 99: KeywordError at line 39, column 10 (length: 5) - 100: Colon at line 39, column 15 (length: 1) - 101: Identifier("log_message") at line 40, column 9 (length: 11) - 102: KeywordWith at line 40, column 21 (length: 4) - 103: Identifier("label") at line 40, column 26 (length: 5) - 104: KeywordWith at line 40, column 32 (length: 4) - 105: StringLiteral(": PASS (caught ") at line 40, column 37 (length: 17) - 106: KeywordWith at line 40, column 55 (length: 4) - 107: KeywordError at line 40, column 60 (length: 5) - 108: KeywordWith at line 40, column 66 (length: 4) - 109: StringLiteral(")") at line 40, column 71 (length: 3) - 110: KeywordEnd at line 41, column 5 (length: 3) - 111: KeywordTry at line 41, column 9 (length: 3) - 112: KeywordEnd at line 42, column 1 (length: 3) - 113: KeywordAction at line 42, column 5 (length: 6) diff --git a/Nexus/test_debug.txt b/Nexus/test_debug.txt deleted file mode 100644 index d33b81b4..00000000 --- a/Nexus/test_debug.txt +++ /dev/null @@ -1,19 +0,0 @@ -=== WFL Debug Report === -Script: ./Nexus/test.wfl -Time: 2025-06-02 02:29:31 - -=== Error Summary === -Runtime error at line 28, column 14: Undefined variable 'expected' - -=== Stack Trace === -In main script at line 28, column 14 - -=== Source Code === - 26: // --------------------------------------------------------------------- - 27: define action called assert_equal needs label expected actual: ->> 28: check if expected is equal to actual: - 29: log_message with label with ": PASS" - 30: otherwise: - -=== Local Variables === -(No local variables in global scope) diff --git a/Nexus/test_mixed_actions.wfl b/Nexus/test_mixed_actions.wfl deleted file mode 100644 index f73e8f3b..00000000 --- a/Nexus/test_mixed_actions.wfl +++ /dev/null @@ -1,18 +0,0 @@ -// Test file for mixed parameter and no-parameter actions - -// No-parameter action -define action called greet: - display "Hello, World from WFL!" -end action - -// Action with parameters -define action called greet_person with name: - display "Hello, " with name with "!" -end action - -// Call the actions -display "Calling no-parameter action..." -greet // This should display "Hello, World from WFL!" - -display "Calling parameterized action..." -greet_person with "John" // This should display "Hello, John!" \ No newline at end of file diff --git a/Nexus/test_multiple_actions.wfl b/Nexus/test_multiple_actions.wfl deleted file mode 100644 index d44bd656..00000000 --- a/Nexus/test_multiple_actions.wfl +++ /dev/null @@ -1,18 +0,0 @@ -// Test file for multiple no-parameter actions - -// First action -define action called greet: - display "Hello, World from WFL!" -end action - -// Second action -define action called farewell: - display "Goodbye from WFL!" -end action - -// Call the actions -display "Calling greet action..." -greet // This should display "Hello, World from WFL!" - -display "Calling farewell action..." -farewell // This should display "Goodbye from WFL!" \ No newline at end of file diff --git a/TODO.md b/TODO.md deleted file mode 100644 index eb240bcb..00000000 --- a/TODO.md +++ /dev/null @@ -1,242 +0,0 @@ -# WFL TODO List - -## 🚨 Critical Issues - -### 1. Runtime Type Conversion Error with "of" Syntax -- **Priority**: HIGH -- **Status**: Parser works correctly, but runtime has issues -- **Description**: Using natural language function calls with "of" syntax (e.g., `path_join of "home" and "user"`) causes runtime error: "Expected text, got Boolean" -- **Location**: Interpreter argument processing -- **Impact**: Prevents full functionality of natural language function calls -- **Workaround**: Use intermediate variables - -### 2. Standard Library Function Call Issues -- **Priority**: HIGH -- **Status**: Functions are registered but parser doesn't handle calls properly -- **Description**: Parser treats expressions like `typeof of number value` as variable names rather than function calls -- **Impact**: Standard library functions cannot be used with natural language syntax -- **Related**: `src/parser/mod.rs:554:44` panic - -## 🔧 Code Improvements - -### Type Checker -- [x] Implement proper static member type lookup (`src/typechecker/mod.rs:1677`) -- [x] Implement proper method type lookup (`src/typechecker/mod.rs:1702`) - -### Code Formatter (Fixer) -- [x] Implement container property and method formatting (`src/fixer/mod.rs:430`) -- [x] Implement property initializer formatting (`src/fixer/mod.rs:447`) -- [x] Implement interface method formatting (`src/fixer/mod.rs:456`) -- [x] Implement event parameter formatting (`src/fixer/mod.rs:467`) - -### Interpreter -- [x] Handle different file open modes (Read, Write, Append) (`src/interpreter/mod.rs:1425`) -- [x] Handle inheritance for containers (`src/interpreter/mod.rs:2081`) -- [x] Call constructor method with arguments (`src/interpreter/mod.rs:2093`) - -## 📚 Documentation Tasks - -### Technical Documentation Updates -- [x] Update parser documentation with recent fixes -- [x] Document bytecode implementation (currently missing) -- [x] Update lexer documentation with Logos implementation details -- [x] Document the analyzer module -- [x] Create architecture diagram showing data flow - -### User Guides -- [x] Create "Getting Started" tutorial -- [x] Write "WFL by Example" guide -- [x] Create cookbook for common tasks -- [x] Write migration guide from other languages - -### API Documentation -- [x] Document all standard library functions with examples -- [x] Create module-specific guides (math, text, list, etc.) -- [x] Document async/await patterns -- [x] Create container system tutorial - -## 🎯 Feature Implementation - -### Parser Enhancements -- [ ] Support method chaining syntax -- [ ] Implement string interpolation -- [ ] Add pattern matching syntax -- [ ] Support lambda/anonymous functions -- [ ] Implement destructuring assignments - -### Standard Library Expansion -- [ ] Implement Time module functions -- [ ] Add JSON parsing/generation module -- [ ] Implement HTTP client module -- [ ] Add Database connectivity module -- [ ] Create Crypto module for hashing/encryption - -### Container System -- [ ] Implement proper inheritance -- [ ] Add interface validation -- [ ] Support static members -- [ ] Implement access modifiers (public/private) -- [ ] Add property getters/setters - -### Async/Concurrent Features -- [ ] Implement proper async/await error handling -- [ ] Add parallel execution constructs -- [ ] Implement channels for communication -- [ ] Add timeout support for async operations -- [ ] Create async standard library functions - -## 🚀 Performance Optimizations - -### Bytecode VM (Planned) -- [ ] Design bytecode instruction set -- [ ] Implement bytecode compiler -- [ ] Create bytecode interpreter -- [ ] Add JIT compilation support -- [ ] Implement bytecode optimizer - -### Current Interpreter -- [ ] Optimize variable lookup -- [ ] Cache function resolutions -- [ ] Improve list operations performance -- [ ] Optimize string concatenation -- [ ] Add memory pooling - -## 🧪 Testing Improvements - -### Test Coverage -- [ ] Add more parser edge case tests -- [ ] Create comprehensive standard library tests -- [ ] Add performance benchmarks -- [ ] Create stress tests for async operations -- [ ] Add property-based testing - -### Test Programs -- [ ] Create test for each standard library function -- [ ] Add container inheritance tests -- [ ] Create async/await edge case tests -- [ ] Add error handling tests -- [ ] Create integration test suite - -## 🛠️ Development Tools - -### LSP Improvements -- [ ] Add refactoring support -- [ ] Implement find references -- [ ] Add rename symbol support -- [ ] Improve hover documentation -- [ ] Add code actions for quick fixes - -### Debugger -- [ ] Implement breakpoint support -- [ ] Add step-through debugging -- [ ] Create variable inspection -- [ ] Add call stack visualization -- [ ] Implement conditional breakpoints - -### REPL Enhancements -- [ ] Add syntax highlighting -- [ ] Implement command history -- [ ] Add tab completion -- [ ] Support multi-line input -- [ ] Add session save/restore - -## 🌐 Web Integration - -### WebAssembly Target -- [ ] Research WASM compilation strategy -- [ ] Implement WASM code generator -- [ ] Create JavaScript interop layer -- [ ] Add DOM manipulation support -- [ ] Create browser runtime - -### Web IDE -- [ ] Design web-based editor -- [ ] Implement syntax highlighting -- [ ] Add real-time error checking -- [ ] Create sharing functionality -- [ ] Add collaborative editing - -## 📦 Package Management - -### Package System Design -- [ ] Define package format -- [ ] Create package manifest schema -- [ ] Implement dependency resolution -- [ ] Add version management -- [ ] Create package registry - -### Build System -- [ ] Implement project scaffolding -- [ ] Add build configuration -- [ ] Create bundling support -- [ ] Add minification options -- [ ] Implement tree shaking - -## 🔒 Security - -### Language Security -- [ ] Implement sandboxing for untrusted code -- [ ] Add resource limits (memory, CPU) -- [ ] Create permission system -- [ ] Add input validation helpers -- [ ] Implement secure defaults - -### Standard Library Security -- [ ] Add crypto functions -- [ ] Implement secure random -- [ ] Add password hashing -- [ ] Create JWT support -- [ ] Add OAuth helpers - -## 📱 Platform Support - -### Cross-Platform -- [ ] Test on macOS -- [ ] Test on Linux distributions -- [ ] Ensure Windows compatibility -- [ ] Add mobile runtime support -- [ ] Create platform-specific APIs - -### Installation -- [ ] Create installers for each platform -- [ ] Add package manager support (brew, apt, choco) -- [ ] Create Docker image -- [ ] Add CI/CD for releases -- [ ] Create auto-update mechanism - -## 📊 Monitoring and Analytics - -### Telemetry -- [ ] Add opt-in usage analytics -- [ ] Implement error reporting -- [ ] Create performance metrics -- [ ] Add feature usage tracking -- [ ] Create dashboard for insights - -### Developer Experience -- [ ] Add first-run experience -- [ ] Create interactive tutorials -- [ ] Implement helpful error suggestions -- [ ] Add code snippets/templates -- [ ] Create learning path - -## Priority Order - -1. **Fix critical runtime issues** (type conversion, function calls) -2. **Complete parser enhancements** for better standard library support -3. **Implement missing TODO items** in existing code -4. **Update documentation** to match current implementation -5. **Expand standard library** with essential modules -6. **Improve developer tools** (LSP, debugger, REPL) -7. **Add web integration** features -8. **Implement performance optimizations** -9. **Create package management** system -10. **Add platform-specific features** - -## Notes - -- All tasks should maintain backward compatibility -- Each feature should include tests and documentation -- Performance impact should be considered for all changes -- User experience is paramount - errors should be helpful -- Follow the existing code style and conventions \ No newline at end of file diff --git a/TestPrograms/basic_syntax_comprehensive.wfl b/TestPrograms/basic_syntax_comprehensive.wfl index e84857d9..10806ac6 100644 --- a/TestPrograms/basic_syntax_comprehensive.wfl +++ b/TestPrograms/basic_syntax_comprehensive.wfl @@ -39,7 +39,7 @@ display "" display "4. Variable Redefinition Test" store test var as "original" display "Before: " with test var -store test var as "modified" +change test var to "modified" display "After: " with test var display "" @@ -78,8 +78,9 @@ count from 0 to 10 by 2: end count display "Loop variable test:" +store loop_message as "" count from 1 to 3: - store loop_message as "Iteration " with count + change loop_message to "Iteration " with count display " " with loop_message end count display "" diff --git a/TestPrograms/basic_syntax_comprehensive_debug.txt b/TestPrograms/basic_syntax_comprehensive_debug.txt new file mode 100644 index 00000000..b6e5bd3c --- /dev/null +++ b/TestPrograms/basic_syntax_comprehensive_debug.txt @@ -0,0 +1,19 @@ +=== WFL Debug Report === +Script: basic_syntax_comprehensive.wfl +Time: 2025-08-11 03:00:26 + +=== Error Summary === +Runtime error at line 103, column 37: Error in native function: Runtime error at line 0, column 0: Expected text, got List + +=== Stack Trace === +In main script at line 103, column 37 + +=== Source Code === + 101: store my numbers as [1 and 2 and 3 and 4 and 5] + 102: display "Number list: " with my numbers +>> 103: display "List length: " with length of my numbers + 104: display "" + 105: + +=== Local Variables === +(No local variables in global scope) diff --git a/TestPrograms/test_length2_debug.txt b/TestPrograms/test_length2_debug.txt new file mode 100644 index 00000000..f5946a15 --- /dev/null +++ b/TestPrograms/test_length2_debug.txt @@ -0,0 +1,19 @@ +=== WFL Debug Report === +Script: TestPrograms/test_length2.wfl +Time: 2025-08-11 03:05:38 + +=== Error Summary === +Runtime error at line 3, column 21: Error in native function: Runtime error at line 0, column 0: Expected text, got List + +=== Stack Trace === +In main script at line 3, column 21 + +=== Source Code === + 1: // Test length function more carefully + 2: store numbers as [1 and 2 and 3] +>> 3: store len as length of numbers + 4: display len + 5: + +=== Local Variables === +(No local variables in global scope) diff --git a/TestPrograms/test_length3_debug.txt b/TestPrograms/test_length3_debug.txt new file mode 100644 index 00000000..03cecb46 --- /dev/null +++ b/TestPrograms/test_length3_debug.txt @@ -0,0 +1,18 @@ +=== WFL Debug Report === +Script: TestPrograms/test_length3.wfl +Time: 2025-08-11 03:06:28 + +=== Error Summary === +Runtime error at line 10, column 22: Error in native function: Runtime error at line 0, column 0: Expected text, got List + +=== Stack Trace === +In main script at line 10, column 22 + +=== Source Code === + 8: // Method 2: Using "of" + 9: store temp_list as numbers +>> 10: store len2 as length of temp_list + 11: display "Method 2: " with len2 + +=== Local Variables === +(No local variables in global scope) diff --git a/TestPrograms/test_length_debug.txt b/TestPrograms/test_length_debug.txt new file mode 100644 index 00000000..2cbf16dc --- /dev/null +++ b/TestPrograms/test_length_debug.txt @@ -0,0 +1,19 @@ +=== WFL Debug Report === +Script: TestPrograms/test_length.wfl +Time: 2025-08-11 03:05:01 + +=== Error Summary === +Runtime error at line 3, column 26: Error in native function: Runtime error at line 0, column 0: Expected text, got List + +=== Stack Trace === +In main script at line 3, column 26 + +=== Source Code === + 1: // Test length function + 2: store numbers as [1 and 2 and 3 and 4 and 5] +>> 3: store list_len as length of numbers + 4: display "List length: " with list_len + 5: + +=== Local Variables === +(No local variables in global scope) diff --git a/TestPrograms/test_redefinition_error.wfl b/TestPrograms/test_redefinition_error.wfl new file mode 100644 index 00000000..5d5311b8 --- /dev/null +++ b/TestPrograms/test_redefinition_error.wfl @@ -0,0 +1,7 @@ +// This should cause an error - redefining a variable with store +store x as 5 +display "First x: " with x + +// This should fail with a helpful error message +store x as 10 +display "Second x: " with x \ No newline at end of file diff --git a/TestPrograms/variable_redefinition.wfl b/TestPrograms/variable_redefinition.wfl new file mode 100644 index 00000000..a8844b5b --- /dev/null +++ b/TestPrograms/variable_redefinition.wfl @@ -0,0 +1,81 @@ +// Test variable redefinition rules +// This test ensures proper use of 'store' and 'change' keywords + +display "=== Variable Redefinition Test ===" +display "" + +// 1. Basic variable definition and change +display "1. Basic variable definition and change:" +store my_var as "initial value" +display "Initial: " with my_var + +change my_var to "updated value" +display "Updated: " with my_var +display "" + +// 2. Multiple changes +display "2. Multiple changes:" +store counter as 0 +display "Initial counter: " with counter + +change counter to 1 +display "After first change: " with counter + +change counter to 2 +display "After second change: " with counter +display "" + +// 3. Working with different types +display "3. Type changes:" +store flexible as "text" +display "As text: " with flexible + +change flexible to 42 +display "As number: " with flexible + +change flexible to yes +display "As boolean: " with flexible +display "" + +// 4. Constants cannot be changed +display "4. Constants (should not be changeable):" +store new constant PI as 3.14159 +display "PI = " with PI +// The following line would cause an error if uncommented: +// change PI to 3.14 + +// 5. Scoped variables +display "5. Variables in scopes:" +store outer_var as "outer" +display "Outer before if: " with outer_var + +check if yes: + // Can change outer variables from inner scope + change outer_var to "changed from inner" + display "Outer in if: " with outer_var + + // New variables in inner scope + store inner_var as "inner only" + display "Inner var: " with inner_var +end check + +display "Outer after if: " with outer_var +// inner_var is not accessible here +display "" + +// 6. Loop variables +display "6. Loop variables:" +store loop_counter as 0 + +count from 1 to 3: + // 'count' is automatically available in loop + display "Loop count: " with count + + // Can change outer variables + change loop_counter to count +end count + +display "Final loop_counter: " with loop_counter +display "" + +display "=== All variable redefinition tests passed ===" \ No newline at end of file diff --git a/another_non_existent.txt b/another_non_existent.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/build_msi_summary.md b/build_msi_summary.md deleted file mode 100644 index 8ab715fc..00000000 --- a/build_msi_summary.md +++ /dev/null @@ -1,65 +0,0 @@ -# WFL MSI Installer Build Process - -## What We've Achieved - -We've created a comprehensive PowerShell script (`build_msi.ps1`) that automates the process of building an MSI installer for the WFL project. The script: - -1. Checks for required dependencies (WiX Toolset, cargo-wix) -2. Creates and configures the necessary config files -3. Generates WiX source files -4. Builds the MSI with the correct version number (2025.4) - -## Current Status - -The script successfully detects if the required dependencies are present and provides helpful instructions if they are not. Currently, the **WiX Toolset** is not installed on this system, which is a prerequisite for building MSI installers. - -## Next Steps - -To successfully build the MSI installer, you would need to: - -1. Install the WiX Toolset (requires administrator privileges): - ```powershell - # Method 1: Using Chocolatey (Run PowerShell as Administrator) - # Install Chocolatey (if not already installed) - Set-ExecutionPolicy Bypass -Scope Process -Force - [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 - Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) - - # Install WiX Toolset - choco install wixtoolset -y - ``` - - Alternative method: - ``` - # Method 2: Direct Download - # Download the installer from https://wixtoolset.org/releases/ and run it - # After installation, ensure the bin directory is in your PATH environment variable - ``` - -2. Run the script again: - ``` - .\build_msi.ps1 - ``` - -3. The resulting MSI will be located at: - ``` - target/x86_64-pc-windows-msvc/release/wfl-2025.4.msi - ``` - -## Key Issues Fixed in the Script - -1. **Incorrect Parameter Syntax**: Changed `--define Version=2025.4` to `-C "-dVersion=2025.4"` -2. **Missing Package Specification**: Added `-p wfl` to specify which package to build in the workspace -3. **Missing WiX Source Files**: Added step to generate WiX source files using `cargo wix init` -4. **Dependency Checking**: Added comprehensive checks for WiX Toolset and cargo-wix - -## Notes on the GitHub Actions Workflow - -The GitHub Actions workflow in `.github/workflows/nightly.yml` uses a slightly different approach by: - -1. Installing WiX Toolset via Chocolatey -2. Installing cargo-wix with a specific version -3. Setting up the target directory structure -4. Building the MSI with cargo-wix - -Our local script is based on this workflow but has been adapted to work better in a manual development environment, with improved error handling and user feedback. diff --git a/clippy_output.txt b/clippy_output.txt deleted file mode 100644 index 0fcdce5d..00000000 --- a/clippy_output.txt +++ /dev/null @@ -1,2328 +0,0 @@ -warning: this `if` statement can be collapsed - --> src\analyzer\mod.rs:471:21 - | -471 | / if defined_in_else.iter().any(|(n, _)| n == name) || else_block.is_none() { -472 | | if let Err(error) = self.current_scope.define(symbol.clone()) { -473 | | self.errors.push(error); -474 | | } -475 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if - = note: `#[warn(clippy::collapsible_if)]` on by default -help: collapse nested if block - | -471 ~ if (defined_in_else.iter().any(|(n, _)| n == name) || else_block.is_none()) { -472 ~ && let Err(error) = self.current_scope.define(symbol.clone()) { -473 | self.errors.push(error); -474 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\mod.rs:479:21 - | -479 | / if !defined_in_then.iter().any(|(n, _)| n == name) { -480 | | if let Err(error) = self.current_scope.define(symbol.clone()) { -481 | | self.errors.push(error); -482 | | } -483 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -479 ~ if !defined_in_then.iter().any(|(n, _)| n == name) -480 ~ && let Err(error) = self.current_scope.define(symbol.clone()) { -481 | self.errors.push(error); -482 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\mod.rs:609:17 - | -609 | / if let Expression::FunctionCall { -610 | | function, -611 | | arguments, -612 | | .. -... | -635 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -613 ~ } = expression -614 ~ && let Expression::Variable(func_name, _, _) = &**function { -615 | if func_name == "push" && arguments.len() >= 2 { -... -632 | } -633 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\mod.rs:615:21 - | -615 | / if let Expression::Variable(func_name, _, _) = &**function { -616 | | if func_name == "push" && arguments.len() >= 2 { -617 | | if let Expression::Variable(list_name, line, column) = -618 | | &arguments[0].value -... | -634 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -615 ~ if let Expression::Variable(func_name, _, _) = &**function -616 ~ && func_name == "push" && arguments.len() >= 2 { -617 | if let Expression::Variable(list_name, line, column) = -... -632 | } -633 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\mod.rs:616:25 - | -616 | / if func_name == "push" && arguments.len() >= 2 { -617 | | if let Expression::Variable(list_name, line, column) = -618 | | &arguments[0].value -... | -633 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -616 ~ if func_name == "push" && arguments.len() >= 2 -617 ~ && let Expression::Variable(list_name, line, column) = -618 | &arguments[0].value -... -631 | } -632 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\mod.rs:617:29 - | -617 | / ... if let Expression::Variable(list_name, line, column) = -618 | | ... &arguments[0].value -619 | | ... { -620 | | ... if self.current_scope.resolve(list_name).is_none() { -... | -632 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -618 ~ &arguments[0].value -619 ~ && self.current_scope.resolve(list_name).is_none() { -620 | let list_symbol = Symbol { -... -629 | } -630 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\static_analyzer.rs:233:21 - | -233 | / if let Expression::Variable(var_name, ..) = &arg.value { -234 | | if let Some(usage) = variable_usages.get_mut(var_name) { -235 | | usage.used = true; -236 | | } -237 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -233 ~ if let Expression::Variable(var_name, ..) = &arg.value -234 ~ && let Some(usage) = variable_usages.get_mut(var_name) { -235 | usage.used = true; -236 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\static_analyzer.rs:333:13 - | -333 | / if let Statement::ActionDefinition { -334 | | name, -335 | | body, -336 | | return_type, -... | -361 | | } - | |_____________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -340 ~ } = statement -341 ~ && let Some(ret_type) = return_type { -342 | if *ret_type != Type::Nothing { -... -358 | } -359 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\static_analyzer.rs:342:17 - | -342 | / if let Some(ret_type) = return_type { -343 | | if *ret_type != Type::Nothing { -344 | | let mut has_return = false; -345 | | let all_paths_return = self.check_all_paths_return(body, &mut has_return); -... | -360 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -342 ~ if let Some(ret_type) = return_type -343 ~ && *ret_type != Type::Nothing { -344 | let mut has_return = false; -... -358 | } -359 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\static_analyzer.rs:679:21 - | -679 | / if let Some(arg_name) = &arg.name { -680 | | if let Some(usage) = usages.get_mut(arg_name) { -681 | | usage.used = true; -682 | | } -683 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -679 ~ if let Some(arg_name) = &arg.name -680 ~ && let Some(usage) = usages.get_mut(arg_name) { -681 | usage.used = true; -682 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\static_analyzer.rs:690:21 - | -690 | / if let Expression::Variable(var_name, ..) = &arg.value { -691 | | if let Some(usage) = usages.get_mut(var_name) { -692 | | usage.used = true; -693 | | } -694 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -690 ~ if let Expression::Variable(var_name, ..) = &arg.value -691 ~ && let Some(usage) = usages.get_mut(var_name) { -692 | usage.used = true; -693 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\static_analyzer.rs:712:17 - | -712 | / if let Expression::Variable(var_name, ..) = &**left { -713 | | if let Some(usage) = usages.get_mut(var_name) { -714 | | usage.used = true; -715 | | } -716 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -712 ~ if let Expression::Variable(var_name, ..) = &**left -713 ~ && let Some(usage) = usages.get_mut(var_name) { -714 | usage.used = true; -715 ~ } - | - -warning: this `if` statement can be collapsed - --> src\analyzer\static_analyzer.rs:718:17 - | -718 | / if let Expression::Variable(var_name, ..) = &**right { -719 | | if let Some(usage) = usages.get_mut(var_name) { -720 | | usage.used = true; -721 | | } -722 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -718 ~ if let Expression::Variable(var_name, ..) = &**right -719 ~ && let Some(usage) = usages.get_mut(var_name) { -720 | usage.used = true; -721 ~ } - | - -warning: this `if` statement can be collapsed - --> src\config.rs:398:5 - | -398 | / if global_config.exists() { -399 | | if let Ok(text) = std::fs::read_to_string(global_config) { -400 | | loaded_global = true; -401 | | log::debug!( -... | -407 | | } - | |_____^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -398 ~ if global_config.exists() -399 ~ && let Ok(text) = std::fs::read_to_string(global_config) { -400 | loaded_global = true; -... -405 | parse_config_text(&mut config, &text, global_config); -406 ~ } - | - -warning: this `if` statement can be collapsed - --> src\config.rs:411:9 - | -411 | / if old_system_config.exists() { -412 | | if let Ok(text) = std::fs::read_to_string(old_system_config) { -413 | | log::debug!( -414 | | "Loading global configuration from {} (legacy path)", -... | -419 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -411 ~ if old_system_config.exists() -412 ~ && let Ok(text) = std::fs::read_to_string(old_system_config) { -413 | log::debug!( -... -417 | parse_config_text(&mut config, &text, old_system_config); -418 ~ } - | - -warning: this `if` statement can be collapsed - --> src\config.rs:423:5 - | -423 | / if local_config.exists() { -424 | | if let Ok(text) = std::fs::read_to_string(&local_config) { -425 | | log::debug!( -426 | | "Loading local configuration from {}", -... | -431 | | } - | |_____^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -423 ~ if local_config.exists() -424 ~ && let Ok(text) = std::fs::read_to_string(&local_config) { -425 | log::debug!( -... -429 | parse_config_text(&mut config, &text, &local_config); -430 ~ } - | - -warning: this `if` statement can be collapsed - --> src\config.rs:444:5 - | -444 | / if global_config.exists() { -445 | | if let Ok(text) = std::fs::read_to_string(global_config) { -446 | | loaded_global = true; -447 | | log::debug!( -... | -453 | | } - | |_____^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -444 ~ if global_config.exists() -445 ~ && let Ok(text) = std::fs::read_to_string(global_config) { -446 | loaded_global = true; -... -451 | parse_config_text(&mut config, &text, global_config); -452 ~ } - | - -warning: this `if` statement can be collapsed - --> src\config.rs:457:9 - | -457 | / if old_system_config.exists() { -458 | | if let Ok(text) = std::fs::read_to_string(old_system_config) { -459 | | log::debug!( -460 | | "Loading global configuration from {} (legacy path)", -... | -465 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -457 ~ if old_system_config.exists() -458 ~ && let Ok(text) = std::fs::read_to_string(old_system_config) { -459 | log::debug!( -... -463 | parse_config_text(&mut config, &text, old_system_config); -464 ~ } - | - -warning: this `if` statement can be collapsed - --> src\config.rs:470:5 - | -470 | / if local_config.exists() { -471 | | if let Ok(text) = std::fs::read_to_string(&local_config) { -472 | | log::debug!( -473 | | "Loading local configuration from {}", -... | -478 | | } - | |_____^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -470 ~ if local_config.exists() -471 ~ && let Ok(text) = std::fs::read_to_string(&local_config) { -472 | log::debug!( -... -476 | parse_config_text(&mut config, &text, &local_config); -477 ~ } - | - -warning: this `if` statement can be collapsed - --> src\interpreter\mod.rs:879:17 - | -879 | / if let Value::Text(text) = &evaluated_value { -880 | | if text.as_ref() == "[]" { -881 | | evaluated_value = Value::List(Rc::new(RefCell::new(Vec::new()))); -882 | | } -883 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -879 ~ if let Value::Text(text) = &evaluated_value -880 ~ && text.as_ref() == "[]" { -881 | evaluated_value = Value::List(Rc::new(RefCell::new(Vec::new()))); -882 ~ } - | - -warning: this `if` statement can be collapsed - --> src\interpreter\mod.rs:1754:29 - | -1754 | / ... if let Some(value) = env.borrow().get(var_name) { -1755 | | ... if !matches!(value, Value::Null) { -1756 | | ... return Ok((Value::Null, ControlFlow::None)); -1757 | | ... } -1758 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1754 ~ if let Some(value) = env.borrow().get(var_name) -1755 ~ && !matches!(value, Value::Null) { -1756 | return Ok((Value::Null, ControlFlow::None)); -1757 ~ } - | - -warning: this `if` statement can be collapsed - --> src\interpreter\mod.rs:3883:25 - | -3883 | / if let Some(ext) = file_ext { -3884 | | if exts.iter().any(|e| e == &ext) { -3885 | | files.push(Value::Text(path_str.into())); -3886 | | } -3887 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -3883 ~ if let Some(ext) = file_ext -3884 ~ && exts.iter().any(|e| e == &ext) { -3885 | files.push(Value::Text(path_str.into())); -3886 ~ } - | - -warning: this `if` statement can be collapsed - --> src\interpreter\mod.rs:3920:17 - | -3920 | / if let Some(ext) = file_ext { -3921 | | if extensions.iter().any(|e| e == &ext) { -3922 | | files.push(Value::Text(path_str.into())); -3923 | | } -3924 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -3920 ~ if let Some(ext) = file_ext -3921 ~ && extensions.iter().any(|e| e == &ext) { -3922 | files.push(Value::Text(path_str.into())); -3923 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:45:13 - | -45 | / if let Some(first_token) = tokens_clone.next() { -46 | | if first_token.token == Token::KeywordEnd { -47 | | if let Some(second_token) = tokens_clone.next() { -48 | | match &second_token.token { -... | -159 | | } - | |_____________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -45 ~ if let Some(first_token) = tokens_clone.next() -46 ~ && first_token.token == Token::KeywordEnd { -47 | if let Some(second_token) = tokens_clone.next() { -... -157 | } -158 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:186:13 - | -186 | / if let Some(token) = self.tokens.peek() { -187 | | if token.token == Token::KeywordEnd && start_len <= 2 { -188 | | // If we're at the end with just 1-2 tokens left, consume them and break -189 | | while self.tokens.next().is_some() {} -... | -192 | | } - | |_____________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -186 ~ if let Some(token) = self.tokens.peek() -187 ~ && token.token == Token::KeywordEnd && start_len <= 2 { -188 | // If we're at the end with just 1-2 tokens left, consume them and break -189 | while self.tokens.next().is_some() {} -190 | break; -191 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:391:9 - | -391 | / if let Some(token) = self.tokens.peek() { -392 | | if matches!(token.token, Token::KeywordConstant) { -393 | | // This is the deprecated "create new constant" syntax -394 | | eprintln!( -... | -412 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -391 ~ if let Some(token) = self.tokens.peek() -392 ~ && matches!(token.token, Token::KeywordConstant) { -393 | // This is the deprecated "create new constant" syntax -... -410 | }); -411 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:635:9 - | -635 | / if let Some(token) = self.tokens.peek() { -636 | | if token.token == Token::KeywordExtends { -637 | | self.tokens.next(); // Consume 'extends' -... | -658 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -635 ~ if let Some(token) = self.tokens.peek() -636 ~ && token.token == Token::KeywordExtends { -637 | self.tokens.next(); // Consume 'extends' -... -656 | } -657 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:661:9 - | -661 | / if let Some(token) = self.tokens.peek() { -662 | | if token.token == Token::KeywordImplements { -663 | | self.tokens.next(); // Consume 'implements' -... | -699 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -661 ~ if let Some(token) = self.tokens.peek() -662 ~ && token.token == Token::KeywordImplements { -663 | self.tokens.next(); // Consume 'implements' -... -697 | } -698 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:904:9 - | -904 | / if let Some(token) = self.tokens.peek() { -905 | | if token.token == Token::KeywordNeeds { -906 | | self.tokens.next(); // Consume 'needs' -907 | | parameters = self.parse_parameter_list()?; -908 | | } -909 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -904 ~ if let Some(token) = self.tokens.peek() -905 ~ && token.token == Token::KeywordNeeds { -906 | self.tokens.next(); // Consume 'needs' -907 | parameters = self.parse_parameter_list()?; -908 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1106:21 - | -1106 | / if let Some(token) = tokens_clone.next() { -1107 | | if token.token == Token::KeywordFile { -1108 | | if let Some(token) = tokens_clone.next() { -1109 | | if token.token == Token::KeywordAt { -... | -1147 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1106 ~ if let Some(token) = tokens_clone.next() -1107 ~ && token.token == Token::KeywordFile { -1108 | if let Some(token) = tokens_clone.next() { - ... -1145 | } -1146 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1107:25 - | -1107 | / if token.token == Token::KeywordFile { -1108 | | if let Some(token) = tokens_clone.next() { -1109 | | if token.token == Token::KeywordAt { -1110 | | if let Some(token) = tokens_clone.next() { -... | -1146 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1107 ~ if token.token == Token::KeywordFile -1108 ~ && let Some(token) = tokens_clone.next() { -1109 | if token.token == Token::KeywordAt { - ... -1144 | } -1145 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1108:29 - | -1108 | / ... if let Some(token) = tokens_clone.next() { -1109 | | ... if token.token == Token::KeywordAt { -1110 | | ... if let Some(token) = tokens_clone.next() { -1111 | | ... if let Token::StringLiteral(_) = token.token { -... | -1145 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1108 ~ if let Some(token) = tokens_clone.next() -1109 ~ && token.token == Token::KeywordAt { -1110 | if let Some(token) = tokens_clone.next() { - ... -1143 | } -1144 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1109:33 - | -1109 | / ... if token.token == Token::KeywordAt { -1110 | | ... if let Some(token) = tokens_clone.next() { -1111 | | ... if let Token::StringLiteral(_) = token.token { -1112 | | ... if let Some(token) = tokens_clone.next() { -... | -1144 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1109 ~ if token.token == Token::KeywordAt -1110 ~ && let Some(token) = tokens_clone.next() { -1111 | if let Token::StringLiteral(_) = token.token { - ... -1142 | } -1143 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1110:37 - | -1110 | / ... if let Some(token) = tokens_clone.next() { -1111 | | ... if let Token::StringLiteral(_) = token.token { -1112 | | ... if let Some(token) = tokens_clone.next() { -1113 | | ... if token.token == Token::KeywordAnd { -... | -1143 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1110 ~ if let Some(token) = tokens_clone.next() -1111 ~ && let Token::StringLiteral(_) = token.token { -1112 | if let Some(token) = tokens_clone.next() { - ... -1141 | } -1142 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1111:41 - | -1111 | / ... if let Token::StringLiteral(_) = token.token { -1112 | | ... if let Some(token) = tokens_clone.next() { -1113 | | ... if token.token == Token::KeywordAnd { -1114 | | ... if let Some(token) = tokens_clone.next() { -... | -1142 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1111 ~ if let Token::StringLiteral(_) = token.token -1112 ~ && let Some(token) = tokens_clone.next() { -1113 | if token.token == Token::KeywordAnd { - ... -1140 | } -1141 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1112:45 - | -1112 | / ... if let Some(token) = tokens_clone.next() { -1113 | | ... if token.token == Token::KeywordAnd { -1114 | | ... if let Some(token) = tokens_clone.next() { -1115 | | ... if token.token == Token::KeywordRead { -... | -1141 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1112 ~ if let Some(token) = tokens_clone.next() -1113 ~ && token.token == Token::KeywordAnd { -1114 | if let Some(token) = tokens_clone.next() { - ... -1139 | } -1140 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1113:49 - | -1113 | / ... if token.token == Token::KeywordAnd { -1114 | | ... if let Some(token) = tokens_clone.next() { -1115 | | ... if token.token == Token::KeywordRead { -1116 | | ... if let Some(token) = tokens_clone.next() -... | -1140 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1113 ~ if token.token == Token::KeywordAnd -1114 ~ && let Some(token) = tokens_clone.next() { -1115 | if token.token == Token::KeywordRead { - ... -1138 | } -1139 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1114:53 - | -1114 | / ... if let Some(token) = tokens_clone.next() { -1115 | | ... if token.token == Token::KeywordRead { -1116 | | ... if let Some(token) = tokens_clone.next() -... | -1139 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1114 ~ if let Some(token) = tokens_clone.next() -1115 ~ && token.token == Token::KeywordRead { -1116 | if let Some(token) = tokens_clone.next() - ... -1137 | } -1138 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1115:57 - | -1115 | / ... if token.token == Token::KeywordRead { -1116 | | ... if let Some(token) = tokens_clone.next() -1117 | | ... { -1118 | | ... if token.token -... | -1138 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1115 ~ if token.token == Token::KeywordRead -1116 ~ && let Some(token) = tokens_clone.next() -1117 | { - ... -1136 | } -1137 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1116:61 - | -1116 | / ... if let Some(token) = tokens_clone.next() -1117 | | ... { -1118 | | ... if token.token -1119 | | ... == Token::KeywordContent -... | -1137 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1116 ~ if let Some(token) = tokens_clone.next() -1117 ~ && token.token -1118 | == Token::KeywordContent - ... -1134 | } -1135 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1118:65 - | -1118 | / ... if token.token -1119 | | ... == Token::KeywordContent -1120 | | ... { -1121 | | ... if let Some(token) = -... | -1136 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1119 ~ == Token::KeywordContent -1120 ~ && let Some(token) = -1121 | tokens_clone.next() - ... -1133 | } -1134 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1121:69 - | -1121 | / ... if let Some(token) = -1122 | | ... tokens_clone.next() -1123 | | ... { -1124 | | ... if token.token -... | -1135 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1122 ~ tokens_clone.next() -1123 ~ && token.token -1124 | == Token::KeywordAs - ... -1132 | } -1133 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1124:73 - | -1124 | / ... if token.token -1125 | | ... == Token::KeywordAs -1126 | | ... { -1127 | | ... if let Some(token) = -... | -1134 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1125 ~ == Token::KeywordAs -1126 ~ && let Some(token) = -1127 | tokens_clone.next() - ... -1131 | } -1132 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1127:77 - | -1127 | / ... if let Some(token) = -1128 | | ... tokens_clone.next() -1129 | | ... { -1130 | | ... if let Token::Identifier(_) = token.token { -... | -1133 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1128 ~ tokens_clone.next() -1129 ~ && let Token::Identifier(_) = token.token { -1130 | has_read_pattern = true; -1131 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1188:9 - | -1188 | / if is_store { -1189 | | if let Some(next_token) = self.tokens.peek() { -1190 | | if matches!(next_token.token, Token::KeywordNew) { -1191 | | self.tokens.next(); // Consume "new" -... | -1215 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1188 ~ if is_store -1189 ~ && let Some(next_token) = self.tokens.peek() { -1190 | if matches!(next_token.token, Token::KeywordNew) { - ... -1213 | } -1214 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1189:13 - | -1189 | / if let Some(next_token) = self.tokens.peek() { -1190 | | if matches!(next_token.token, Token::KeywordNew) { -1191 | | self.tokens.next(); // Consume "new" -1192 | | if let Some(const_token) = self.tokens.peek() { -... | -1214 | | } - | |_____________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1189 ~ if let Some(next_token) = self.tokens.peek() -1190 ~ && matches!(next_token.token, Token::KeywordNew) { -1191 | self.tokens.next(); // Consume "new" - ... -1212 | } -1213 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1567:21 - | -1567 | / if let Expression::Variable(ref name, var_line, var_column) = left { -1568 | | if self.known_actions.contains(name) { -1569 | | // This is a known action, treat it as an action call -1570 | | self.tokens.next(); // Consume "with" -... | -1581 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1567 ~ if let Expression::Variable(ref name, var_line, var_column) = left -1568 ~ && self.known_actions.contains(name) { -1569 | // This is a known action, treat it as an action call - ... -1579 | continue; // Skip the rest of the loop since we've already updated left -1580 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1602:21 - | -1602 | / if let Some(equal_token) = self.tokens.peek().cloned() { -1603 | | if matches!(equal_token.token, Token::KeywordEqual) { -1604 | | self.tokens.next(); // Consume "equal" -... | -1641 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1602 ~ if let Some(equal_token) = self.tokens.peek().cloned() -1603 ~ && matches!(equal_token.token, Token::KeywordEqual) { -1604 | self.tokens.next(); // Consume "equal" - ... -1639 | } -1640 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1606:29 - | -1606 | / ... if let Some(to_token) = self.tokens.peek().cloned() { -1607 | | ... if matches!(to_token.token, Token::KeywordTo) { -1608 | | ... self.tokens.next(); // Consume "to" -... | -1639 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1606 ~ if let Some(to_token) = self.tokens.peek().cloned() -1607 ~ && matches!(to_token.token, Token::KeywordTo) { -1608 | self.tokens.next(); // Consume "to" - ... -1637 | } -1638 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1649:21 - | -1649 | / if let Some(pattern_token) = self.tokens.peek().cloned() { -1650 | | if matches!(pattern_token.token, Token::KeywordPattern) { -1651 | | self.tokens.next(); // Consume "pattern" -1652 | | } -1653 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1649 ~ if let Some(pattern_token) = self.tokens.peek().cloned() -1650 ~ && matches!(pattern_token.token, Token::KeywordPattern) { -1651 | self.tokens.next(); // Consume "pattern" -1652 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1669:21 - | -1669 | / if let Some(pattern_token) = self.tokens.peek().cloned() { -1670 | | if matches!(pattern_token.token, Token::KeywordPattern) { -1671 | | self.tokens.next(); // Consume "pattern" -1672 | | } -1673 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1669 ~ if let Some(pattern_token) = self.tokens.peek().cloned() -1670 ~ && matches!(pattern_token.token, Token::KeywordPattern) { -1671 | self.tokens.next(); // Consume "pattern" -1672 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1677:21 - | -1677 | / if let Some(in_token) = self.tokens.peek().cloned() { -1678 | | if matches!(in_token.token, Token::KeywordIn) { -1679 | | self.tokens.next(); // Consume "in" -... | -1691 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1677 ~ if let Some(in_token) = self.tokens.peek().cloned() -1678 ~ && matches!(in_token.token, Token::KeywordIn) { -1679 | self.tokens.next(); // Consume "in" - ... -1689 | continue; // Skip the rest of the loop since we've already updated left -1690 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1705:21 - | -1705 | / if let Some(pattern_token) = self.tokens.peek().cloned() { -1706 | | if matches!(pattern_token.token, Token::KeywordPattern) { -1707 | | self.tokens.next(); // Consume "pattern" -1708 | | } -1709 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1705 ~ if let Some(pattern_token) = self.tokens.peek().cloned() -1706 ~ && matches!(pattern_token.token, Token::KeywordPattern) { -1707 | self.tokens.next(); // Consume "pattern" -1708 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1713:21 - | -1713 | / if let Some(with_token) = self.tokens.peek().cloned() { -1714 | | if matches!(with_token.token, Token::KeywordWith) { -1715 | | self.tokens.next(); // Consume "with" -... | -1745 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1713 ~ if let Some(with_token) = self.tokens.peek().cloned() -1714 ~ && matches!(with_token.token, Token::KeywordWith) { -1715 | self.tokens.next(); // Consume "with" - ... -1743 | continue; // Skip the rest of the loop since we've already updated left -1744 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1719:29 - | -1719 | / ... if let Some(in_token) = self.tokens.peek().cloned() { -1720 | | ... if matches!(in_token.token, Token::KeywordIn) { -1721 | | ... self.tokens.next(); // Consume "in" -... | -1734 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1719 ~ if let Some(in_token) = self.tokens.peek().cloned() -1720 ~ && matches!(in_token.token, Token::KeywordIn) { -1721 | self.tokens.next(); // Consume "in" - ... -1732 | continue; // Skip the rest of the loop since we've already updated left -1733 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1759:21 - | -1759 | / if let Some(on_token) = self.tokens.peek().cloned() { -1760 | | if matches!(on_token.token, Token::KeywordOn) { -1761 | | self.tokens.next(); // Consume "on" -... | -1780 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1759 ~ if let Some(on_token) = self.tokens.peek().cloned() -1760 ~ && matches!(on_token.token, Token::KeywordOn) { -1761 | self.tokens.next(); // Consume "on" - ... -1778 | continue; // Skip the rest of the loop since we've already updated left -1779 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1764:29 - | -1764 | / ... if let Some(pattern_token) = self.tokens.peek().cloned() { -1765 | | ... if matches!(pattern_token.token, Token::KeywordPattern) { -1766 | | ... self.tokens.next(); // Consume "pattern" -1767 | | ... } -1768 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1764 ~ if let Some(pattern_token) = self.tokens.peek().cloned() -1765 ~ && matches!(pattern_token.token, Token::KeywordPattern) { -1766 | self.tokens.next(); // Consume "pattern" -1767 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1791:21 - | -1791 | / if let Some(pattern_token) = self.tokens.peek().cloned() { -1792 | | if matches!(pattern_token.token, Token::KeywordPattern) { -1793 | | self.tokens.next(); // Consume "pattern" -... | -1805 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1791 ~ if let Some(pattern_token) = self.tokens.peek().cloned() -1792 ~ && matches!(pattern_token.token, Token::KeywordPattern) { -1793 | self.tokens.next(); // Consume "pattern" - ... -1803 | continue; // Skip the rest of the loop since we've already updated left -1804 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1845:21 - | -1845 | / if let Some(next_token) = self.tokens.peek() { -1846 | | if next_token.token == Token::RightBracket { -1847 | | self.tokens.next(); // Consume ']' -1848 | | return Ok(Expression::Literal( -... | -1854 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1845 ~ if let Some(next_token) = self.tokens.peek() -1846 ~ && next_token.token == Token::RightBracket { -1847 | self.tokens.next(); // Consume ']' - ... -1852 | )); -1853 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1968:37 - | -1968 | / ... if let Some(paren_token) = self.tokens.peek().cloned() { -1969 | | ... if paren_token.token == Token::LeftParen { -1970 | | ... self.tokens.next(); // Consume '(' -... | -2015 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1968 ~ if let Some(paren_token) = self.tokens.peek().cloned() -1969 ~ && paren_token.token == Token::LeftParen { -1970 | self.tokens.next(); // Consume '(' - ... -2013 | }); -2014 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:1974:45 - | -1974 | / ... if let Some(next_token) = self.tokens.peek() { -1975 | | ... if next_token.token != Token::RightParen { -1976 | | ... let expr = self.parse_expression()?; -1977 | | ... arguments.push(Argument { -... | -1996 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -1974 ~ if let Some(next_token) = self.tokens.peek() -1975 ~ && next_token.token != Token::RightParen { -1976 | let expr = self.parse_expression()?; - ... -1994 | } -1995 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2042:32 - | -2042 | } else if let Token::Identifier(id) = &next_token.token { - | ________________________________^ -2043 | | if id.to_lowercase() == "with" { -2044 | | self.tokens.next(); // Consume "with" -... | -2057 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2042 ~ } else if let Token::Identifier(id) = &next_token.token -2043 ~ && id.to_lowercase() == "with" { -2044 | self.tokens.next(); // Consume "with" - ... -2055 | }); -2056 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2212:21 - | -2212 | / if let Some(next_token) = self.tokens.peek() { -2213 | | if next_token.token == Token::KeywordExists { -2214 | | self.tokens.next(); // Consume "exists" -2215 | | self.expect_token( -... | -2226 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2212 ~ if let Some(next_token) = self.tokens.peek() -2213 ~ && next_token.token == Token::KeywordExists { -2214 | self.tokens.next(); // Consume "exists" - ... -2224 | }); -2225 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2241:21 - | -2241 | / if let Some(next_token) = self.tokens.peek() { -2242 | | if next_token.token == Token::KeywordExists { -2243 | | self.tokens.next(); // Consume "exists" -2244 | | self.expect_token( -... | -2255 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2241 ~ if let Some(next_token) = self.tokens.peek() -2242 ~ && next_token.token == Token::KeywordExists { -2243 | self.tokens.next(); // Consume "exists" - ... -2253 | }); -2254 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2270:21 - | -2270 | / if let Some(next_token) = self.tokens.peek() { -2271 | | if next_token.token == Token::KeywordFiles { -2272 | | self.tokens.next(); // Consume "files" -2273 | | self.expect_token( -... | -2328 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2270 ~ if let Some(next_token) = self.tokens.peek() -2271 ~ && next_token.token == Token::KeywordFiles { -2272 | self.tokens.next(); // Consume "files" - ... -2326 | }); -2327 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2286:41 - | -2286 | / ... if let Some(with_token) = self.tokens.peek() { -2287 | | ... if with_token.token == Token::KeywordWith { -2288 | | ... self.tokens.next(); // Consume "with" -2289 | | ... let extensions = self.parse_extension_filter()?; -... | -2297 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2286 ~ if let Some(with_token) = self.tokens.peek() -2287 ~ && with_token.token == Token::KeywordWith { -2288 | self.tokens.next(); // Consume "with" - ... -2295 | }); -2296 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2343:21 - | -2343 | / if let Some(next_token) = self.tokens.peek() { -2344 | | if next_token.token == Token::KeywordContent { -2345 | | self.tokens.next(); // Consume "content" -2346 | | self.expect_token( -... | -2357 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2343 ~ if let Some(next_token) = self.tokens.peek() -2344 ~ && next_token.token == Token::KeywordContent { -2345 | self.tokens.next(); // Consume "content" - ... -2355 | }); -2356 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2713:9 - | -2713 | / if let Some(token) = self.tokens.peek() { -2714 | | if matches!(token.token, Token::Colon) { -2715 | | self.tokens.next(); // Consume the colon if present -2716 | | } -2717 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2713 ~ if let Some(token) = self.tokens.peek() -2714 ~ && matches!(token.token, Token::Colon) { -2715 | self.tokens.next(); // Consume the colon if present -2716 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2738:17 - | -2738 | / if let Some(token) = self.tokens.peek() { -2739 | | if matches!(token.token, Token::Colon) { -2740 | | self.tokens.next(); // Consume the colon if present -2741 | | } -2742 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2738 ~ if let Some(token) = self.tokens.peek() -2739 ~ && matches!(token.token, Token::Colon) { -2740 | self.tokens.next(); // Consume the colon if present -2741 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:2963:9 - | -2963 | / if let Some(token) = self.tokens.peek() { -2964 | | if matches!(token.token, Token::Colon) { -2965 | | self.tokens.next(); // Consume the colon if present -2966 | | } -2967 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -2963 ~ if let Some(token) = self.tokens.peek() -2964 ~ && matches!(token.token, Token::Colon) { -2965 | self.tokens.next(); // Consume the colon if present -2966 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:3058:9 - | -3058 | / if let Some(token) = self.tokens.peek() { -3059 | | if matches!(token.token, Token::Colon) { -3060 | | self.tokens.next(); // Consume the colon if present -3061 | | } -3062 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -3058 ~ if let Some(token) = self.tokens.peek() -3059 ~ && matches!(token.token, Token::Colon) { -3060 | self.tokens.next(); // Consume the colon if present -3061 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:3142:9 - | -3142 | / if let Some(token) = self.tokens.peek().cloned() { -3143 | | if matches!(token.token, Token::KeywordNeeds) -3144 | | || matches!(token.token, Token::KeywordWith) -... | -3247 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -3142 ~ if let Some(token) = self.tokens.peek().cloned() -3143 ~ && (matches!(token.token, Token::KeywordNeeds) -3144 ~ || matches!(token.token, Token::KeywordWith)) -3145 | { - ... -3245 | } -3246 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:3295:9 - | -3295 | / if let Some(token) = self.tokens.peek().cloned() { -3296 | | if let Token::Identifier(id) = &token.token { -3297 | | if id == "and" { -3298 | | self.tokens.next(); // Consume the extra "and" -... | -3301 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -3295 ~ if let Some(token) = self.tokens.peek().cloned() -3296 ~ && let Token::Identifier(id) = &token.token { -3297 | if id == "and" { -3298 | self.tokens.next(); // Consume the extra "and" -3299 | } -3300 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:3296:13 - | -3296 | / if let Token::Identifier(id) = &token.token { -3297 | | if id == "and" { -3298 | | self.tokens.next(); // Consume the extra "and" -3299 | | } -3300 | | } - | |_____________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -3296 ~ if let Token::Identifier(id) = &token.token -3297 ~ && id == "and" { -3298 | self.tokens.next(); // Consume the extra "and" -3299 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:3576:21 - | -3576 | / if let Some(token) = self.tokens.peek().cloned() { -3577 | | if token.token == Token::KeywordAt { -3578 | | self.tokens.next(); // Consume "at" -... | -3677 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -3576 ~ if let Some(token) = self.tokens.peek().cloned() -3577 ~ && token.token == Token::KeywordAt { -3578 | self.tokens.next(); // Consume "at" - ... -3675 | } -3676 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:3704:9 - | -3704 | / if let Some(token) = self.tokens.peek().cloned() { -3705 | | if token.token == Token::KeywordAt { -3706 | | self.tokens.next(); // Consume "at" -... | -3840 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -3704 ~ if let Some(token) = self.tokens.peek().cloned() -3705 ~ && token.token == Token::KeywordAt { -3706 | self.tokens.next(); // Consume "at" - ... -3838 | } -3839 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4040:9 - | -4040 | / if let Some(next_token) = self.tokens.peek() { -4041 | | if next_token.token == Token::KeywordFile { -4042 | | self.tokens.next(); // Consume "file" -4043 | | } -4044 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4040 ~ if let Some(next_token) = self.tokens.peek() -4041 ~ && next_token.token == Token::KeywordFile { -4042 | self.tokens.next(); // Consume "file" -4043 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4382:21 - | -4382 | / if let Some(token) = self.tokens.peek() { -4383 | | if matches!(token.token, Token::Colon) { -4384 | | self.tokens.next(); // Consume the colon if present -4385 | | } -4386 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4382 ~ if let Some(token) = self.tokens.peek() -4383 ~ && matches!(token.token, Token::Colon) { -4384 | self.tokens.next(); // Consume the colon if present -4385 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4409:21 - | -4409 | / if let Some(token) = self.tokens.peek() { -4410 | | if matches!(token.token, Token::Colon) { -4411 | | self.tokens.next(); // Consume the colon if present -4412 | | } -4413 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4409 ~ if let Some(token) = self.tokens.peek() -4410 ~ && matches!(token.token, Token::Colon) { -4411 | self.tokens.next(); // Consume the colon if present -4412 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4497:9 - | -4497 | / if let Some(token) = self.tokens.peek().cloned() { -4498 | | if let Token::Identifier(id) = &token.token { -4499 | | if id.to_lowercase() == "loop" { -4500 | | self.tokens.next(); // Consume "loop" -... | -4503 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4497 ~ if let Some(token) = self.tokens.peek().cloned() -4498 ~ && let Token::Identifier(id) = &token.token { -4499 | if id.to_lowercase() == "loop" { -4500 | self.tokens.next(); // Consume "loop" -4501 | } -4502 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4498:13 - | -4498 | / if let Token::Identifier(id) = &token.token { -4499 | | if id.to_lowercase() == "loop" { -4500 | | self.tokens.next(); // Consume "loop" -4501 | | } -4502 | | } - | |_____________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4498 ~ if let Token::Identifier(id) = &token.token -4499 ~ && id.to_lowercase() == "loop" { -4500 | self.tokens.next(); // Consume "loop" -4501 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4529:9 - | -4529 | / if let Some(token) = self.tokens.peek() { -4530 | | if token.line == start_line && !Parser::is_statement_starter(&token.token) { -4531 | | // so we can continue parsing the expression -4532 | | value_expr = self.parse_binary_expression(0)?; -4533 | | } -4534 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4529 ~ if let Some(token) = self.tokens.peek() -4530 ~ && token.line == start_line && !Parser::is_statement_starter(&token.token) { -4531 | // so we can continue parsing the expression -4532 | value_expr = self.parse_binary_expression(0)?; -4533 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4574:9 - | -4574 | / if let Some(token) = self.tokens.peek().cloned() { -4575 | | if matches!(token.token, Token::KeywordNeeds) { -4576 | | self.tokens.next(); // Consume "needs" -4577 | | parameters = self.parse_parameter_list()?; -4578 | | } -4579 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4574 ~ if let Some(token) = self.tokens.peek().cloned() -4575 ~ && matches!(token.token, Token::KeywordNeeds) { -4576 | self.tokens.next(); // Consume "needs" -4577 | parameters = self.parse_parameter_list()?; -4578 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4647:21 - | -4647 | / if let Some(next_token) = tokens_clone.next() { -4648 | | if next_token.token == Token::KeywordPattern { -4649 | | depth -= 1; -4650 | | if depth == 0 { -... | -4656 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4647 ~ if let Some(next_token) = tokens_clone.next() -4648 ~ && next_token.token == Token::KeywordPattern { -4649 | depth -= 1; - ... -4654 | } -4655 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4663:21 - | -4663 | / if let Some(next_token) = tokens_clone.next() { -4664 | | if next_token.token == Token::KeywordPattern { -4665 | | depth += 1; -4666 | | } -4667 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4663 ~ if let Some(next_token) = tokens_clone.next() -4664 ~ && next_token.token == Token::KeywordPattern { -4665 | depth += 1; -4666 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4816:13 - | -4816 | / if *i < tokens.len() { -4817 | | if let Token::Identifier(s) = &tokens[*i].token { -4818 | | if s == "followed" && *i + 1 < tokens.len() { -4819 | | if let Token::KeywordBy = tokens[*i + 1].token { -... | -4825 | | } - | |_____________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4816 ~ if *i < tokens.len() -4817 ~ && let Token::Identifier(s) = &tokens[*i].token { -4818 | if s == "followed" && *i + 1 < tokens.len() { - ... -4823 | } -4824 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4817:17 - | -4817 | / if let Token::Identifier(s) = &tokens[*i].token { -4818 | | if s == "followed" && *i + 1 < tokens.len() { -4819 | | if let Token::KeywordBy = tokens[*i + 1].token { -4820 | | *i += 2; // Skip "followed by" -... | -4824 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4817 ~ if let Token::Identifier(s) = &tokens[*i].token -4818 ~ && s == "followed" && *i + 1 < tokens.len() { -4819 | if let Token::KeywordBy = tokens[*i + 1].token { - ... -4822 | } -4823 ~ } - | - -warning: this `if` statement can be collapsed - --> src\parser\mod.rs:4818:21 - | -4818 | / if s == "followed" && *i + 1 < tokens.len() { -4819 | | if let Token::KeywordBy = tokens[*i + 1].token { -4820 | | *i += 2; // Skip "followed by" -4821 | | continue; -4822 | | } -4823 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -4818 ~ if s == "followed" && *i + 1 < tokens.len() -4819 ~ && let Token::KeywordBy = tokens[*i + 1].token { -4820 | *i += 2; // Skip "followed by" -4821 | continue; -4822 ~ } - | - -warning: this `if` statement can be collapsed - --> src\pattern\vm.rs:671:29 - | -671 | / ... if let Ok(result) = -672 | | ... lookbehind_vm.execute(lookbehind_program, &text_slice) -673 | | ... { -674 | | ... if result { -... | -690 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -672 ~ lookbehind_vm.execute(lookbehind_program, &text_slice) -673 ~ && result { -674 | // Check if the match uses the entire slice -... -687 | } -688 ~ } - | - -warning: this `if` statement can be collapsed - --> src\pattern\vm.rs:681:37 - | -681 | / ... if let Some(first_match) = matches.first() { -682 | | ... if first_match.start == 0 -683 | | ... && first_match.end == text_slice.len() -... | -688 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -681 ~ if let Some(first_match) = matches.first() -682 ~ && first_match.start == 0 -683 | && first_match.end == text_slice.len() -... -686 | break; -687 ~ } - | - -warning: this `if` statement can be collapsed - --> src\pattern\vm.rs:721:29 - | -721 | / ... if let Ok(result) = -722 | | ... lookbehind_vm.execute(lookbehind_program, &text_slice) -723 | | ... { -724 | | ... if result { -... | -740 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -722 ~ lookbehind_vm.execute(lookbehind_program, &text_slice) -723 ~ && result { -724 | // Check if the match uses the entire slice -... -737 | } -738 ~ } - | - -warning: this `if` statement can be collapsed - --> src\pattern\vm.rs:731:37 - | -731 | / ... if let Some(first_match) = matches.first() { -732 | | ... if first_match.start == 0 -733 | | ... && first_match.end == text_slice.len() -... | -738 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -731 ~ if let Some(first_match) = matches.first() -732 ~ && first_match.start == 0 -733 | && first_match.end == text_slice.len() -... -736 | break; -737 ~ } - | - -warning: this `if` statement can be collapsed - --> src\typechecker\mod.rs:39:9 - | -39 | / if let Some(expected) = &self.expected { -40 | | if let Some(found) = &self.found { -41 | | message.push_str(&format!(" - Expected {expected} but found {found}")); -42 | | } -43 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -39 ~ if let Some(expected) = &self.expected -40 ~ && let Some(found) = &self.found { -41 | message.push_str(&format!(" - Expected {expected} but found {found}")); -42 ~ } - | - -warning: this `if` statement can be collapsed - --> src\typechecker\mod.rs:130:9 - | -130 | / if !self.analyzer_already_run { -131 | | if let Err(semantic_errors) = self.analyzer.analyze(program) { -132 | | for error in semantic_errors { -133 | | self.errors.push(TypeError::new( -... | -143 | | } - | |_________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -130 ~ if !self.analyzer_already_run -131 ~ && let Err(semantic_errors) = self.analyzer.analyze(program) { -132 | for error in semantic_errors { -... -141 | return Err(self.errors.clone()); -142 ~ } - | - -warning: this `if` statement can be collapsed - --> src\typechecker\mod.rs:255:17 - | -255 | / if !variable_name.is_empty() { -256 | | if let Some(symbol) = self.analyzer.get_symbol_mut(variable_name) { -257 | | symbol.symbol_type = Some(Type::Text); -258 | | } -259 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -255 ~ if !variable_name.is_empty() -256 ~ && let Some(symbol) = self.analyzer.get_symbol_mut(variable_name) { -257 | symbol.symbol_type = Some(Type::Text); -258 ~ } - | - -warning: this `if` statement can be collapsed - --> src\typechecker\mod.rs:281:17 - | -281 | / if !variable_name.is_empty() { -282 | | if let Some(symbol) = self.analyzer.get_symbol_mut(variable_name) { -283 | | symbol.symbol_type = Some(Type::Text); -284 | | } -285 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -281 ~ if !variable_name.is_empty() -282 ~ && let Some(symbol) = self.analyzer.get_symbol_mut(variable_name) { -283 | symbol.symbol_type = Some(Type::Text); -284 ~ } - | - -warning: this `if` statement can be collapsed - --> src\typechecker\mod.rs:334:17 - | -334 | / if inferred_type != Type::Error && inferred_type != Type::Unknown { -335 | | if let Some(symbol) = self.analyzer.get_symbol_mut(name) { -336 | | if symbol.symbol_type.is_none() { -337 | | symbol.symbol_type = Some(inferred_type); -... | -340 | | } - | |_________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -334 ~ if inferred_type != Type::Error && inferred_type != Type::Unknown -335 ~ && let Some(symbol) = self.analyzer.get_symbol_mut(name) { -336 | if symbol.symbol_type.is_none() { -337 | symbol.symbol_type = Some(inferred_type); -338 | } -339 ~ } - | - -warning: this `if` statement can be collapsed - --> src\typechecker\mod.rs:335:21 - | -335 | / if let Some(symbol) = self.analyzer.get_symbol_mut(name) { -336 | | if symbol.symbol_type.is_none() { -337 | | symbol.symbol_type = Some(inferred_type); -338 | | } -339 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -335 ~ if let Some(symbol) = self.analyzer.get_symbol_mut(name) -336 ~ && symbol.symbol_type.is_none() { -337 | symbol.symbol_type = Some(inferred_type); -338 ~ } - | - -warning: this `if` statement can be collapsed - --> src\typechecker\mod.rs:363:28 - | -363 | } else if inferred_type != Type::Error && inferred_type != Type::Unknown { - | ____________________________^ -364 | | if let Some(symbol) = self.analyzer.get_symbol_mut(name) { -365 | | symbol.symbol_type = Some(inferred_type); -366 | | } -367 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -363 ~ } else if inferred_type != Type::Error && inferred_type != Type::Unknown -364 ~ && let Some(symbol) = self.analyzer.get_symbol_mut(name) { -365 | symbol.symbol_type = Some(inferred_type); -366 ~ } - | - -warning: this `if` statement can be collapsed - --> src\typechecker\mod.rs:878:25 - | -878 | / if let Some(declared_type) = &property.property_type { -879 | | if !self.are_types_compatible(&default_type, declared_type) { -880 | | self.type_error( -881 | | format!( -... | -890 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -878 ~ if let Some(declared_type) = &property.property_type -879 ~ && !self.are_types_compatible(&default_type, declared_type) { -880 | self.type_error( -... -888 | ); -889 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:349:25 - | -349 | / if let Some(valid_values) = &setting.valid_values { -350 | | if !valid_values.contains(&value.to_string()) { -351 | | issues.push(ConfigIssue { -352 | | file_path: file_path.to_path_buf(), -... | -365 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -349 ~ if let Some(valid_values) = &setting.valid_values -350 ~ && !valid_values.contains(&value.to_string()) { -351 | issues.push(ConfigIssue { -... -363 | }); -364 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:368:25 - | -368 | / if let Some(valid_values) = &setting.valid_values { -369 | | if !valid_values.contains(&value.to_string()) { -370 | | issues.push(ConfigIssue { -371 | | file_path: file_path.to_path_buf(), -... | -384 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -368 ~ if let Some(valid_values) = &setting.valid_values -369 ~ && !valid_values.contains(&value.to_string()) { -370 | issues.push(ConfigIssue { -... -382 | }); -383 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:474:21 - | -474 | / if let Some(line_number) = issue.line_number { -475 | | if line_number <= lines.len() { -476 | | lines[line_number - 1] = -477 | | format!("# {} (unknown key)", lines[line_number - 1]); -... | -480 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -474 ~ if let Some(line_number) = issue.line_number -475 ~ && line_number <= lines.len() { -476 | lines[line_number - 1] = -477 | format!("# {} (unknown key)", lines[line_number - 1]); -478 | println!("✅ Commented out unknown key at line {line_number}"); -479 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:483:21 - | -483 | / if let (Some(line_number), Some(setting_name)) = -484 | | (issue.line_number, &issue.setting_name) -485 | | { -486 | | if line_number <= lines.len() { -... | -497 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -484 ~ (issue.line_number, &issue.setting_name) -485 ~ && line_number <= lines.len() { -486 | if let Some(setting) = self.expected_settings.get(setting_name) { -... -494 | } -495 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:486:25 - | -486 | / if line_number <= lines.len() { -487 | | if let Some(setting) = self.expected_settings.get(setting_name) { -488 | | if let Some(default_value) = &setting.default_value { -489 | | lines[line_number - 1] = -... | -496 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -486 ~ if line_number <= lines.len() -487 ~ && let Some(setting) = self.expected_settings.get(setting_name) { -488 | if let Some(default_value) = &setting.default_value { -... -494 | } -495 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:487:29 - | -487 | / ... if let Some(setting) = self.expected_settings.get(setting_name) { -488 | | ... if let Some(default_value) = &setting.default_value { -489 | | ... lines[line_number - 1] = -490 | | ... format!("{setting_name} = {default_value}"); -... | -495 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -487 ~ if let Some(setting) = self.expected_settings.get(setting_name) -488 ~ && let Some(default_value) = &setting.default_value { -489 | lines[line_number - 1] = -... -493 | ); -494 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:500:21 - | -500 | / if let Some(setting_name) = &issue.setting_name { -501 | | if let Some(setting) = self.expected_settings.get(setting_name) { -502 | | if let Some(default_value) = &setting.default_value { -503 | | if !added_settings.contains_key(setting_name) { -... | -512 | | } - | |_____________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -500 ~ if let Some(setting_name) = &issue.setting_name -501 ~ && let Some(setting) = self.expected_settings.get(setting_name) { -502 | if let Some(default_value) = &setting.default_value { -... -510 | } -511 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:501:25 - | -501 | / if let Some(setting) = self.expected_settings.get(setting_name) { -502 | | if let Some(default_value) = &setting.default_value { -503 | | if !added_settings.contains_key(setting_name) { -504 | | lines.push(String::new()); -... | -511 | | } - | |_________________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -501 ~ if let Some(setting) = self.expected_settings.get(setting_name) -502 ~ && let Some(default_value) = &setting.default_value { -503 | if !added_settings.contains_key(setting_name) { -... -509 | } -510 ~ } - | - -warning: this `if` statement can be collapsed - --> src\wfl_config\checker.rs:502:29 - | -502 | / ... if let Some(default_value) = &setting.default_value { -503 | | ... if !added_settings.contains_key(setting_name) { -504 | | ... lines.push(String::new()); -505 | | ... lines.push(format!("# {}", setting.description)); -... | -510 | | ... } - | |_______________________^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -502 ~ if let Some(default_value) = &setting.default_value -503 ~ && !added_settings.contains_key(setting_name) { -504 | lines.push(String::new()); -... -508 | println!("✅ Added missing setting: {setting_name}"); -509 ~ } - | - -warning: this `if` statement can be collapsed - --> src\lib.rs:39:5 - | -39 | / if config.logging_enabled { -40 | | if let Err(e) = logging::init_logger(config.log_level, log_path) { -41 | | eprintln!("Failed to initialize logger: {e}"); -42 | | } -43 | | } - | |_____^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -39 ~ if config.logging_enabled -40 ~ && let Err(e) = logging::init_logger(config.log_level, log_path) { -41 | eprintln!("Failed to initialize logger: {e}"); -42 ~ } - | - -warning: this `if` statement can be collapsed - --> src\lib.rs:46:5 - | -46 | / if config.execution_logging { -47 | | if let Err(e) = logging::init_execution_logger(&config, log_path) { -48 | | eprintln!("Failed to initialize execution logger: {e}"); -49 | | } -50 | | } - | |_____^ - | - = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if -help: collapse nested if block - | -46 ~ if config.execution_logging -47 ~ && let Err(e) = logging::init_execution_logger(&config, log_path) { -48 | eprintln!("Failed to initialize execution logger: {e}"); -49 ~ } - | - -warning: `wfl` (lib) generated 111 warnings (run `cargo clippy --fix --lib -p wfl` to apply 111 suggestions) - Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s diff --git a/debug_lookahead.txt b/debug_lookahead.txt deleted file mode 100644 index 48822802..00000000 --- a/debug_lookahead.txt +++ /dev/null @@ -1,36 +0,0 @@ -warning: associated functions `compile_pattern_to_ir`, `parse_sequence`, `parse_element`, and `parse_quantified_content` are never used - --> src\parser\mod.rs:4508:8 - | -17 | impl<'a> Parser<'a> { - | ------------------- associated functions in this implementation -... -4508 | fn compile_pattern_to_ir(tokens: &[TokenWithPosition]) -> Result { - | ^^^^^^^^^^^^^^^^^^^^^ -... -4527 | fn parse_sequence( - | ^^^^^^^^^^^^^^ -... -4550 | fn parse_element(tokens: &[TokenWithPosition], i: &mut usize) -> Result { - | ^^^^^^^^^^^^^ -... -4765 | fn parse_quantified_content( - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(dead_code)]` on by default - -warning: `wfl` (lib) generated 1 warning - Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.29s - Running `target\debug\wfl.exe TestPrograms/debug_lookahead_bytecode.wfl --debug` -error[ANALYZE-SEMANTIC]: Variable 'test_pattern' is not defined - -error[ANALYZE-SEMANTIC]: Variable 'test_pattern' is not defined - -Type checking warnings: -Type error at line 13, column 32: Variable 'test_pattern' is not defined -Type error at line 14, column 32: Variable 'test_pattern' is not defined -Type error at line 13, column 32: Variable 'test_pattern' is not defined -Type error at line 14, column 32: Variable 'test_pattern' is not defined -Debug: Testing lookahead bytecode generation --------------------------------------------- -✓ '5a' matched (correct) -✗ '59' matched (incorrect - should not match) diff --git a/debug_output.txt b/debug_output.txt deleted file mode 100644 index e47a1366..00000000 --- a/debug_output.txt +++ /dev/null @@ -1,6 +0,0 @@ -First: Azusa -Second: Nakano -Third: is -Fourth: cute -Joined states: Azusa -Test variable: Nakano diff --git a/dhat-heap.json b/dhat-heap.json deleted file mode 100644 index 5807083c..00000000 --- a/dhat-heap.json +++ /dev/null @@ -1,2797 +0,0 @@ -{ -"dhatFileVersion": 2, -"mode": "rust-heap", -"verb": "Allocated", -"bklt": true, -"bkacc": false, -"tu": "µs", -"Mtu": "s", -"tuth": 10, -"cmd": "target\\release\\wfl.exe test.wfl", -"pid": 41680, -"tg": 1108, -"te": 5717, -"pps": [ -{ -"tb": 1024, -"tbk": 1, -"tl": 4874, -"mb": 1024, -"mbk": 1, -"gb": 1024, -"gbk": 1, -"eb": 1024, -"ebk": 1, -"fs": [ -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 68, -"tbk": 1, -"tl": 4764, -"mb": 68, -"mbk": 1, -"gb": 68, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 288, -"tbk": 1, -"tl": 4695, -"mb": 288, -"mbk": 1, -"gb": 288, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -50, -51, -52, -53, -54, -55, -56, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 68, -"tbk": 1, -"tl": 4666, -"mb": 68, -"mbk": 1, -"gb": 68, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -57, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 94, -"tbk": 1, -"tl": 25, -"mb": 94, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -62, -63, -64, -65, -66, -67, -68, -69, -70, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 4883, -"mb": 9, -"mbk": 1, -"gb": 9, -"gbk": 1, -"eb": 9, -"ebk": 1, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -72, -73, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 864, -"tbk": 1, -"tl": 24, -"mb": 864, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -75, -76, -77, -78, -79, -80, -45, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 8, -"tbk": 1, -"tl": 5615, -"mb": 8, -"mbk": 1, -"gb": 8, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -81, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61, -82, -83, -84, -85, -86 -] -}, -{ -"tb": 4, -"tbk": 1, -"tl": 1, -"mb": 4, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -87, -88, -89, -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 192, -"tbk": 2, -"tl": 0, -"mb": 128, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -100, -101, -102, -103, -104, -105, -106, -107, -108, -109, -110, -111, -112, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 12, -"mb": 9, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -75, -76, -113, -114, -115, -116, -117, -118, -119, -120, -121, -122, -123, -124, -125, -126, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 56, -"tbk": 1, -"tl": 1, -"mb": 56, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -100, -127, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61, -82, -83, -84, -85, -86 -] -}, -{ -"tb": 984, -"tbk": 1, -"tl": 4539, -"mb": 984, -"mbk": 1, -"gb": 984, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -128, -129, -130, -131, -132, -133, -134, -135, -136, -137, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 5, -"tbk": 1, -"tl": 4675, -"mb": 5, -"mbk": 1, -"gb": 5, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -138, -139, -140, -141, -142, -143, -144, -145, -146, -147, -148, -149, -150, -151, -152, -112, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 8, -"tbk": 1, -"tl": 5624, -"mb": 8, -"mbk": 1, -"gb": 8, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -153, -154, -155, -156, -157, -158, -159, -160, -161, -162, -163, -164, -165, -166, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 212, -"tbk": 1, -"tl": 4893, -"mb": 212, -"mbk": 1, -"gb": 212, -"gbk": 1, -"eb": 212, -"ebk": 1, -"fs": [ -100, -167, -168, -169, -170, -171, -172, -173, -174, -175, -176, -177, -178, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 8, -"tbk": 1, -"tl": 59, -"mb": 8, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -62, -63, -179, -180, -181, -182, -183, -184, -185, -186, -187, -188, -189, -190, -191, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32 -] -}, -{ -"tb": 8192, -"tbk": 1, -"tl": 5647, -"mb": 8192, -"mbk": 1, -"gb": 8192, -"gbk": 1, -"eb": 8192, -"ebk": 1, -"fs": [ -192, -193, -194, -195, -196, -197, -198, -199, -200, -201, -202, -203, -204, -205, -206, -207, -208, -209, -210, -211, -212, -213, -214, -215, -216, -217, -218, -219, -220, -221, -222, -223, -224, -225, -226 -] -}, -{ -"tb": 224, -"tbk": 1, -"tl": 4663, -"mb": 224, -"mbk": 1, -"gb": 224, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -75, -76, -227, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 22, -"tbk": 1, -"tl": 5636, -"mb": 22, -"mbk": 1, -"gb": 22, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -153, -154, -155, -156, -157, -158, -159, -160, -161, -228, -229, -230, -165, -166, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 32, -"tbk": 1, -"tl": 5642, -"mb": 32, -"mbk": 1, -"gb": 32, -"gbk": 1, -"eb": 32, -"ebk": 1, -"fs": [ -231, -232, -233, -199, -200, -201, -202, -203, -204, -205, -206, -207, -208, -209, -210, -211, -212, -213, -214, -215, -216, -217, -218, -219, -220, -221, -222, -223, -224, -225, -226 -] -}, -{ -"tb": 28, -"tbk": 1, -"tl": 28, -"mb": 28, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -62, -63, -64, -65, -66, -67, -68, -234, -235, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 4900, -"mb": 9, -"mbk": 1, -"gb": 9, -"gbk": 1, -"eb": 9, -"ebk": 1, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -72, -178, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 8, -"tbk": 1, -"tl": 4705, -"mb": 8, -"mbk": 1, -"gb": 8, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -236, -237, -238, -56, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 301, -"tbk": 1, -"tl": 5119, -"mb": 301, -"mbk": 1, -"gb": 301, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -62, -63, -64, -65, -66, -67, -68, -239, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 6, -"tbk": 1, -"tl": 4616, -"mb": 6, -"mbk": 1, -"gb": 6, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -240, -241, -242, -243, -244, -245, -246, -247, -248, -249, -250, -251, -252, -253, -254, -255, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 24, -"tbk": 2, -"tl": 3, -"mb": 16, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -62, -63, -256, -257, -258, -259, -260, -261, -262, -165, -166, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 28, -"tbk": 1, -"tl": 13, -"mb": 28, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -263, -264, -265, -266, -267, -268, -269, -270, -271, -272, -273, -274, -275, -276, -277, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 72, -"tbk": 2, -"tl": 477, -"mb": 54, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -263, -264, -265, -266, -267, -268, -269, -278, -279, -280, -281, -282, -283, -284, -239, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 19, -"tbk": 1, -"tl": 4674, -"mb": 19, -"mbk": 1, -"gb": 19, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -285, -286, -287, -288, -289, -290, -291, -292, -293, -294, -295, -296, -297, -298, -112, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 704, -"tbk": 11, -"tl": 61758, -"mb": 704, -"mbk": 11, -"gb": 704, -"gbk": 11, -"eb": 704, -"ebk": 11, -"fs": [ -62, -63, -299, -300, -301, -302, -303, -304, -305, -306, -307, -308, -309, -310, -311, -312, -313, -314, -314, -315 -] -}, -{ -"tb": 19, -"tbk": 1, -"tl": 4663, -"mb": 19, -"mbk": 1, -"gb": 19, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -316, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 8, -"tbk": 1, -"tl": 4, -"mb": 8, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -317, -137, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 68, -"tbk": 2, -"tl": 8, -"mb": 52, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -263, -264, -265, -266, -267, -268, -269, -270, -271, -272, -273, -274, -318, -319, -320, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 46, -"tbk": 1, -"tl": 1, -"mb": 46, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -263, -264, -265, -266, -267, -268, -269, -321, -322, -323, -324, -325, -326, -327, -328, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32 -] -}, -{ -"tb": 160, -"tbk": 1, -"tl": 4792, -"mb": 160, -"mbk": 1, -"gb": 160, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -100, -329, -330, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61, -82, -83, -84, -85, -86 -] -}, -{ -"tb": 28, -"tbk": 1, -"tl": 10, -"mb": 28, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -263, -264, -265, -266, -267, -268, -269, -278, -279, -280, -281, -282, -283, -284, -234, -235, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 120, -"tbk": 4, -"tl": 22, -"mb": 64, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -62, -63, -256, -257, -258, -331, -332, -333, -334, -165, -166, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 40, -"tbk": 1, -"tl": 4765, -"mb": 40, -"mbk": 1, -"gb": 40, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -100, -335, -44, -45, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 4880, -"mb": 9, -"mbk": 1, -"gb": 9, -"gbk": 1, -"eb": 9, -"ebk": 1, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -336, -73, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 384, -"tbk": 1, -"tl": 4607, -"mb": 384, -"mbk": 1, -"gb": 384, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -337, -338, -339, -340, -137, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 4902, -"mb": 9, -"mbk": 1, -"gb": 9, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -341, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 68, -"tbk": 1, -"tl": 4684, -"mb": 68, -"mbk": 1, -"gb": 68, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -342, -112, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 8, -"tbk": 1, -"tl": 1054, -"mb": 8, -"mbk": 1, -"gb": 8, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -343, -137, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 160, -"tbk": 1, -"tl": 4537, -"mb": 160, -"mbk": 1, -"gb": 160, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -344, -345, -346, -347, -137, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 68, -"tbk": 2, -"tl": 8, -"mb": 52, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -263, -264, -265, -266, -267, -268, -269, -278, -279, -280, -281, -282, -283, -284, -69, -70, -71, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 96, -"tbk": 2, -"tl": 4697, -"mb": 64, -"mbk": 1, -"gb": 64, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -348, -349, -350, -351, -352, -353, -354, -355, -356, -357, -358, -359, -55, -56, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 68, -"tbk": 1, -"tl": 19, -"mb": 68, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -360, -112, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 11, -"mb": 9, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -100, -361, -126, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 4878, -"mb": 9, -"mbk": 1, -"gb": 9, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -362, -363, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 114, -"tbk": 2, -"tl": 4775, -"mb": 76, -"mbk": 1, -"gb": 76, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -364, -365, -366, -367, -368, -369, -370, -371, -372, -373, -374, -45, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 672, -"tbk": 2, -"tl": 4891, -"mb": 448, -"mbk": 1, -"gb": 448, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -375, -376, -377, -378, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 301, -"tbk": 1, -"tl": 4704, -"mb": 301, -"mbk": 1, -"gb": 301, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -379, -380, -381, -56, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 104, -"tbk": 3, -"tl": 46, -"mb": 64, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -100, -382, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61, -82, -83, -84, -85, -86 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 9, -"mb": 9, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -383, -374, -45, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 5, -"tbk": 1, -"tl": 4665, -"mb": 5, -"mbk": 1, -"gb": 5, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -384, -58, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61 -] -}, -{ -"tb": 160, -"tbk": 1, -"tl": 4673, -"mb": 160, -"mbk": 1, -"gb": 160, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -344, -345, -346, -385, -386, -298, -112, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 128, -"tbk": 1, -"tl": 11, -"mb": 128, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -62, -63, -387, -388, -389, -390, -391, -392, -393, -165, -166, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 555, -"tbk": 4, -"tl": 48, -"mb": 296, -"mbk": 1, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -48, -49, -75, -76, -394, -395, -396, -397, -398, -399, -400, -401, -402, -403, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33 -] -}, -{ -"tb": 462, -"tbk": 11, -"tl": 48, -"mb": 126, -"mbk": 3, -"gb": 0, -"gbk": 0, -"eb": 0, -"ebk": 0, -"fs": [ -263, -264, -265, -266, -267, -268, -269, -404, -405, -406, -314, -314, -315 -] -}, -{ -"tb": 9, -"tbk": 1, -"tl": 4898, -"mb": 9, -"mbk": 1, -"gb": 9, -"gbk": 1, -"eb": 9, -"ebk": 1, -"fs": [ -34, -35, -36, -37, -38, -39, -40, -41, -42, -336, -178, -74, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47 -] -}, -{ -"tb": 96, -"tbk": 1, -"tl": 5619, -"mb": 96, -"mbk": 1, -"gb": 96, -"gbk": 1, -"eb": 0, -"ebk": 0, -"fs": [ -100, -407, -408, -409, -410, -411, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -46, -47, -59, -60, -61, -82, -83, -84, -85, -86 -] -} -], -"ftbl": [ -"[root]", -"0x7ff78d5028b3: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d5028b3: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d5028b3: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d5028b3: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d5028b3: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d5028b3: alloc::vec::Vec::with_capacity (src\\vec\\mod.rs:495:0)", -"0x7ff78d5028b3: std::io::buffered::bufwriter::BufWriter::with_capacity (io\\buffered\\bufwriter.rs:122:0)", -"0x7ff78d5028b3: std::io::buffered::linewriter::LineWriter::with_capacity (io\\buffered\\linewriter.rs:110:0)", -"0x7ff78d5028b3: std::io::buffered::linewriter::LineWriter::new (io\\buffered\\linewriter.rs:90:0)", -"0x7ff78d5028b3: std::io::stdio::stdout::closure$0 (src\\io\\stdio.rs:719:0)", -"0x7ff78d5028b3: std::sync::once_lock::impl$0::get_or_init::closure$0 (src\\sync\\once_lock.rs:310:0)", -"0x7ff78d5028b3: std::sync::once_lock::impl$0::initialize::closure$0 (src\\sync\\once_lock.rs:518:0)", -"0x7ff78d5028b3: std::sync::poison::once::impl$2::call_once_force::closure$0 > >,std::s (src\\ops\\function.rs:250:0)", -"0x7ff78d55678f: std::sys::sync::once::futex::Once::call (sync\\once\\futex.rs:176:0)", -"0x7ff78d55638e: std::sync::poison::once::Once::call_once_force (sync\\poison\\once.rs:214:0)", -"0x7ff78d55638e: std::sync::once_lock::OnceLock::initialize > >,std::sync::once_lock::impl$0::get_or_init::closure_env$0 > (src\\runtime\\park.rs:284:0)", -"0x7ff78d157f80: tokio::runtime::context::blocking::BlockingRegionGuard::block_on (runtime\\context\\blocking.rs:66:0)", -"0x7ff78d157f80: tokio::runtime::scheduler::multi_thread::impl$0::block_on::closure$0 (scheduler\\multi_thread\\mod.rs:87:0)", -"0x7ff78d157f80: tokio::runtime::context::runtime::enter_runtime >,enum2$,std::io::error::Error> > > (runtime\\context\\runtime.rs:65:0)", -"0x7ff78d15ca65: tokio::runtime::scheduler::multi_thread::MultiThread::block_on (scheduler\\multi_thread\\mod.rs:86:0)", -"0x7ff78d15ca65: tokio::runtime::runtime::Runtime::block_on_inner (src\\runtime\\runtime.rs:370:0)", -"0x7ff78d15ca65: tokio::runtime::runtime::Runtime::block_on > (src\\runtime\\runtime.rs:342:0)", -"0x7ff78d140f11: wfl::main (wfl\\src\\main.rs:327:0)", -"0x7ff78d52f3ad: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d52f3ad: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d52f3ad: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d52f3ad: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d52f3ad: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d52f3ad: alloc::slice::impl$0::to_vec_in::impl$1::to_vec (alloc\\src\\slice.rs:446:0)", -"0x7ff78d52f3ad: alloc::slice::impl$0::to_vec_in (alloc\\src\\slice.rs:399:0)", -"0x7ff78d52f3ad: alloc::vec::impl$11::clone (src\\vec\\mod.rs:3312:0)", -"0x7ff78d52f3ad: alloc::string::impl$6::clone (alloc\\src\\string.rs:2254:0)", -"0x7ff78d1dfb26: alloc::vec::impl$11::clone (src\\vec\\mod.rs:3312:0)", -"0x7ff78d19a490: wfl::parser::Parser::parse (src\\parser\\mod.rs:40:0)", -"0x7ff78d108f50: wfl::main::async_block$0 (wfl\\src\\main.rs:486:0)", -"0x7ff78d15f4e6: core::ops::function::FnOnce::call_once (src\\ops\\function.rs:250:0)", -"0x7ff78d15f4e6: std::sys::backtrace::__rust_begin_short_backtrace,std::io::error::Error> > (*)(),enum2$,std::io::error::Error> > > (src\\sys\\backtrace.rs:152:0)", -"0x7ff78d54111a: alloc::alloc::impl$1::grow (alloc\\src\\alloc.rs:283:0)", -"0x7ff78d54111a: alloc::raw_vec::finish_grow (src\\raw_vec\\mod.rs:781:0)", -"0x7ff78d1928bf: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d1928bf: alloc::raw_vec::RawVecInner::grow_one (src\\raw_vec\\mod.rs:571:0)", -"0x7ff78d1928bf: alloc::raw_vec::RawVec,alloc::alloc::Global>::grow_one,alloc::alloc::Global> (src\\raw_vec\\mod.rs:340:0)", -"0x7ff78d149f96: alloc::vec::Vec,alloc::alloc::Global>::push (src\\vec\\mod.rs:2448:0)", -"0x7ff78d149f96: codespan_reporting::files::SimpleFiles::add (codespan-reporting-0.11.1\\src\\files.rs:373:0)", -"0x7ff78d147bb7: wfl::diagnostics::DiagnosticReporter::add_file,ref$ > (src\\diagnostics\\mod.rs:151:0)", -"0x7ff78d108ffb: wfl::main::async_block$0 (wfl\\src\\main.rs:591:0)", -"0x7ff78d195724: wfl::diagnostics::DiagnosticReporter::report_diagnostic (src\\diagnostics\\mod.rs:156:0)", -"0x7ff78d1090cd: wfl::main::async_block$0 (wfl\\src\\main.rs:595:0)", -"0x7ff78d14f14c: std::rt::lang_start::closure$0 (std\\src\\rt.rs:199:0)", -"0x7ff78d14f14c: core::ops::function::FnOnce::call_once (src\\ops\\function.rs:250:0)", -"0x7ff78d14f14c: core::ops::function::FnOnce::call_once,std::io::error::Error> > >,tuple$<> > (std\\src\\rt.rs:199:0)", -"0x7ff78d555c8d: alloc::alloc::impl$1::grow (alloc\\src\\alloc.rs:283:0)", -"0x7ff78d555c8d: alloc::raw_vec::finish_grow (src\\raw_vec\\mod.rs:781:0)", -"0x7ff78d52f2d0: alloc::raw_vec::RawVecInner::try_reserve_exact (src\\raw_vec\\mod.rs:607:0)", -"0x7ff78d52f2d0: alloc::raw_vec::RawVec::try_reserve_exact (src\\raw_vec\\mod.rs:381:0)", -"0x7ff78d52f2d0: alloc::vec::Vec::try_reserve_exact (src\\vec\\mod.rs:1408:0)", -"0x7ff78d52f2d0: alloc::string::String::try_reserve_exact (alloc\\src\\string.rs:1337:0)", -"0x7ff78d50b1ea: std::fs::read_to_string::inner (std\\src\\fs.rs:316:0)", -"0x7ff78d166e6e: std::fs::read_to_string (std\\src\\fs.rs:320:0)", -"0x7ff78d166e6e: wfl::config::load_config (wfl\\src\\config.rs:362:0)", -"0x7ff78d108bb7: wfl::main::async_block$0 (wfl\\src\\main.rs:325:0)", -"0x7ff78d16e6b1: wfl::lexer::intern_string (src\\lexer\\mod.rs:18:0)", -"0x7ff78d16f20c: wfl::lexer::lex_wfl_with_positions (src\\lexer\\mod.rs:125:0)", -"0x7ff78d108edc: wfl::main::async_block$0 (wfl\\src\\main.rs:482:0)", -"0x7ff78d5411fa: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d5411fa: alloc::raw_vec::impl$4::reserve::do_reserve_and_handle (src\\raw_vec\\mod.rs:557:0)", -"0x7ff78d19a5a9: alloc::raw_vec::RawVecInner::reserve (src\\raw_vec\\mod.rs:563:0)", -"0x7ff78d19a5a9: alloc::raw_vec::RawVec,alloc::alloc::Global>::reserve (src\\raw_vec\\mod.rs:331:0)", -"0x7ff78d19a5a9: alloc::vec::Vec,alloc::alloc::Global>::reserve (src\\vec\\mod.rs:1297:0)", -"0x7ff78d19a5a9: wfl::parser::Parser::parse (src\\parser\\mod.rs:25:0)", -"0x7ff78d1082a0: wfl::main::async_block$0 (wfl\\src\\main.rs:216:0)", -"0x7ff78d5088ec: std::rt::lang_start_internal::closure$0 (std\\src\\rt.rs:168:0)", -"0x7ff78d5088ec: std::panicking::try::do_call (std\\src\\panicking.rs:589:0)", -"0x7ff78d5088ec: std::panicking::try (std\\src\\panicking.rs:552:0)", -"0x7ff78d5088ec: std::panic::catch_unwind (std\\src\\panic.rs:359:0)", -"0x7ff78d5088ec: std::rt::lang_start_internal (std\\src\\rt.rs:164:0)", -"0x7ff78d52d845: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d52d845: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d52d845: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d52d845: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d52d845: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d52d845: alloc::vec::Vec::with_capacity (src\\vec\\mod.rs:495:0)", -"0x7ff78d52d845: alloc::str::convert_while_ascii (alloc\\src\\str.rs:642:0)", -"0x7ff78d52d845: alloc::str::impl$5::to_lowercase (alloc\\src\\str.rs:384:0)", -"0x7ff78d16489a: wfl::config::impl$3::from_str (wfl\\src\\config.rs:79:0)", -"0x7ff78d1650b5: core::str::impl$0::parse (src\\str\\mod.rs:2600:0)", -"0x7ff78d1650b5: wfl::config::LogLevel::parse_str (wfl\\src\\config.rs:92:0)", -"0x7ff78d1650b5: wfl::config::parse_config_text (wfl\\src\\config.rs:201:0)", -"0x7ff78d166f8d: wfl::config::load_config (wfl\\src\\config.rs:367:0)", -"0x7ff78d39abd8: dhat::impl$7::alloc (dhat-0.3.3\\src\\lib.rs:1176:0)", -"0x7ff78d1e1e56: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:480:0)", -"0x7ff78d1e1e56: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d1e1e56: alloc::raw_vec::RawVec,alloc::alloc::Global>::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d1e1e56: alloc::vec::Vec,alloc::alloc::Global>::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d1e1e56: alloc::vec::Vec,alloc::alloc::Global>::with_capacity (src\\vec\\mod.rs:495:0)", -"0x7ff78d1e1e56: alloc::vec::spec_from_iter_nested::impl$0::from_iter (src\\vec\\spec_from_iter_nested.rs:31:0)", -"0x7ff78d1e1e56: alloc::vec::spec_from_iter::impl$0::from_iter,core::str::iter::Lines> (src\\vec\\spec_from_iter.rs:34:0)", -"0x7ff78d196456: alloc::vec::impl$15::from_iter (src\\vec\\mod.rs:3438:0)", -"0x7ff78d196456: core::iter::traits::iterator::Iterator::collect (iter\\traits\\iterator.rs:1985:0)", -"0x7ff78d196456: wfl::diagnostics::DiagnosticReporter::line_col_to_offset (src\\diagnostics\\mod.rs:185:0)", -"0x7ff78d1966ee: wfl::diagnostics::DiagnosticReporter::convert_parse_error (src\\diagnostics\\mod.rs:208:0)", -"0x7ff78d1090bf: wfl::main::async_block$0 (wfl\\src\\main.rs:594:0)", -"0x7ff78d203377: alloc::raw_vec::RawVecInner::reserve (src\\raw_vec\\mod.rs:563:0)", -"0x7ff78d203377: alloc::raw_vec::RawVec::reserve (src\\raw_vec\\mod.rs:331:0)", -"0x7ff78d203377: alloc::vec::Vec::reserve (src\\vec\\mod.rs:1297:0)", -"0x7ff78d203377: alloc::vec::Vec::append_elements (src\\vec\\mod.rs:2592:0)", -"0x7ff78d203377: alloc::vec::spec_extend::impl$4::spec_extend (src\\vec\\spec_extend.rs:61:0)", -"0x7ff78d203377: alloc::vec::Vec::extend_from_slice (src\\vec\\mod.rs:3059:0)", -"0x7ff78d203377: alloc::string::String::push_str (alloc\\src\\string.rs:1112:0)", -"0x7ff78d203377: alloc::str::impl$5::replace (alloc\\src\\str.rs:294:0)", -"0x7ff78d203377: wfl::lexer::token::parse_string (src\\lexer\\token.rs:185:0)", -"0x7ff78d203377: wfl::lexer::token::impl$2::lex::goto91_ctx92_x::callback (src\\lexer\\token.rs:161:0)", -"0x7ff78d203377: wfl::lexer::token::impl$2::lex::goto91_ctx92_x (src\\lexer\\token.rs:3:0)", -"0x7ff78d203377: wfl::lexer::token::impl$2::lex::goto92_ctx92_x (src\\lexer\\token.rs:3:0)", -"0x7ff78d203377: wfl::lexer::token::impl$2::lex::goto93_ctx92_x (src\\lexer\\token.rs:3:0)", -"0x7ff78d16f014: wfl::lexer::lex_wfl_with_positions (src\\lexer\\mod.rs:106:0)", -"0x7ff78d195917: wfl::diagnostics::DiagnosticReporter::report_diagnostic (src\\diagnostics\\mod.rs:163:0)", -"0x7ff78d1d148c: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d1d148c: alloc::boxed::impl$1::try_new_uninit_in (alloc\\src\\boxed.rs:508:0)", -"0x7ff78d1d148c: alloc::boxed::impl$1::new_uninit_in (alloc\\src\\boxed.rs:474:0)", -"0x7ff78d1d148c: alloc::collections::btree::node::LeafNode::new (collections\\btree\\node.rs:83:0)", -"0x7ff78d1d148c: alloc::collections::btree::node::NodeRef,usize,codespan_reporting::term::views::impl$0::render::Line,enum2$ >::new_leaf (collections\\btree\\node.rs:217:0)", -"0x7ff78d1d148c: alloc::collections::btree::map::entry::VacantEntry::insert_entry (btree\\map\\entry.rs:404:0)", -"0x7ff78d1d148c: alloc::collections::btree::map::entry::VacantEntry::insert (btree\\map\\entry.rs:377:0)", -"0x7ff78d1d148c: enum2$ >::or_insert_with::render > (src\\term\\views.rs:151:0)", -"0x7ff78d195d1f: wfl::diagnostics::DiagnosticReporter::report_diagnostic (src\\diagnostics\\mod.rs:175:0)", -"0x7ff78d194962: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d194962: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d194962: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d194962: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d194962: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d194962: alloc::slice::impl$0::to_vec_in::impl$1::to_vec (alloc\\src\\slice.rs:446:0)", -"0x7ff78d194962: alloc::slice::impl$0::to_vec_in (alloc\\src\\slice.rs:399:0)", -"0x7ff78d194962: alloc::slice::impl$0::to_vec (alloc\\src\\slice.rs:375:0)", -"0x7ff78d194962: alloc::slice::impl$9::to_owned (alloc\\src\\slice.rs:841:0)", -"0x7ff78d194962: alloc::str::impl$4::to_owned (alloc\\src\\str.rs:211:0)", -"0x7ff78d194962: alloc::string::impl$47::from (alloc\\src\\string.rs:2943:0)", -"0x7ff78d194962: alloc::string::impl$111::spec_to_string (alloc\\src\\string.rs:2864:0)", -"0x7ff78d194962: alloc::string::impl$34::to_string (alloc\\src\\string.rs:2747:0)", -"0x7ff78d194962: wfl::diagnostics::WflDiagnostic::error (src\\diagnostics\\mod.rs:86:0)", -"0x7ff78d196713: wfl::diagnostics::DiagnosticReporter::convert_parse_error (src\\diagnostics\\mod.rs:213:0)", -"0x7ff78d510b3b: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d510b3b: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d510b3b: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d510b3b: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d510b3b: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d510b3b: alloc::vec::Vec::with_capacity (src\\vec\\mod.rs:495:0)", -"0x7ff78d510b3b: std::sys_common::wtf8::Wtf8Buf::with_capacity (src\\sys_common\\wtf8.rs:194:0)", -"0x7ff78d510b3b: std::sys_common::wtf8::Wtf8Buf::from_wide (src\\sys_common\\wtf8.rs:236:0)", -"0x7ff78d510b3b: std::os::windows::ffi::impl$0::from_wide (os\\windows\\ffi.rs:94:0)", -"0x7ff78d50a94e: std::sys::pal::windows::args::parse_lp_cmd_line (pal\\windows\\args.rs:151:0)", -"0x7ff78d50a94e: std::sys::pal::windows::args::args (pal\\windows\\args.rs:26:0)", -"0x7ff78d50a94e: std::env::args_os (std\\src\\env.rs:863:0)", -"0x7ff78d50a413: std::env::args (std\\src\\env.rs:828:0)", -"0x7ff78d107e6c: wfl::main::async_block$0 (wfl\\src\\main.rs:60:0)", -"0x7ff78d541ee9: hashbrown::raw::RawTableInner::fallible_with_capacity (src\\raw\\mod.rs:1484:0)", -"0x7ff78d541ee9: hashbrown::raw::RawTableInner::prepare_resize (src\\raw\\mod.rs:2551:0)", -"0x7ff78d541ee9: hashbrown::raw::RawTableInner::resize_inner (src\\raw\\mod.rs:2749:0)", -"0x7ff78d541ee9: hashbrown::raw::RawTableInner::reserve_rehash_inner (src\\raw\\mod.rs:2637:0)", -"0x7ff78d541ee9: hashbrown::raw::RawTable,alloc::alloc::Global>::reserve_rehash,alloc::alloc::Global,hashbrown::map::make_hasher::closure_env$0,alloc::alloc::Global>::reserve (src\\raw\\mod.rs:902:0)", -"0x7ff78d1f4bae: hashbrown::raw::RawTable,alloc::alloc::Global>::find_or_find_insert_slot (src\\raw\\mod.rs:1115:0)", -"0x7ff78d1f4bae: hashbrown::map::HashMap::find_or_find_insert_slot (hashbrown-0.15.2\\src\\map.rs:1812:0)", -"0x7ff78d1f4bae: hashbrown::map::HashMap::insert (hashbrown-0.15.2\\src\\map.rs:1792:0)", -"0x7ff78d16e6dd: std::collections::hash::map::HashMap::insert (collections\\hash\\map.rs:1202:0)", -"0x7ff78d16e6dd: wfl::lexer::intern_string (src\\lexer\\mod.rs:18:0)", -"0x7ff78d16f408: wfl::lexer::lex_wfl_with_positions (src\\lexer\\mod.rs:143:0)", -"0x7ff78d555d61: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d555d61: alloc::raw_vec::impl$4::reserve::do_reserve_and_handle (src\\raw_vec\\mod.rs:557:0)", -"0x7ff78d516680: alloc::raw_vec::RawVecInner::reserve (src\\raw_vec\\mod.rs:563:0)", -"0x7ff78d516680: alloc::raw_vec::RawVec::reserve (src\\raw_vec\\mod.rs:331:0)", -"0x7ff78d516680: alloc::vec::Vec::reserve (src\\vec\\mod.rs:1297:0)", -"0x7ff78d516680: alloc::vec::Vec::append_elements (src\\vec\\mod.rs:2592:0)", -"0x7ff78d516680: alloc::vec::spec_extend::impl$4::spec_extend (src\\vec\\spec_extend.rs:61:0)", -"0x7ff78d516680: alloc::vec::Vec::extend_from_slice (src\\vec\\mod.rs:3059:0)", -"0x7ff78d516680: std::sys_common::wtf8::Wtf8Buf::push_wtf8 (src\\sys_common\\wtf8.rs:395:0)", -"0x7ff78d51354b: std::path::PathBuf::push (std\\src\\path.rs:1291:0)", -"0x7ff78d51354b: std::path::Path::_join (std\\src\\path.rs:2689:0)", -"0x7ff78d166e26: std::path::Path::join (std\\src\\path.rs:2684:0)", -"0x7ff78d166e26: wfl::config::load_config (wfl\\src\\config.rs:360:0)", -"0x7ff78d4f999d: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d4f999d: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d4f999d: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d4f999d: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d4f999d: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d4f999d: alloc::vec::Vec::with_capacity (src\\vec\\mod.rs:495:0)", -"0x7ff78d4f999d: parking_lot_core::parking_lot::HashTable::new (parking_lot_core-0.9.10\\src\\parking_lot.rs:75:0)", -"0x7ff78d4f9d9a: parking_lot_core::parking_lot::grow_hashtable (parking_lot_core-0.9.10\\src\\parking_lot.rs:294:0)", -"0x7ff78d4f9d9a: parking_lot_core::parking_lot::ThreadData::new (parking_lot_core-0.9.10\\src\\parking_lot.rs:180:0)", -"0x7ff78d555227: core::ops::function::FnOnce::call_once (src\\ops\\function.rs:250:0)", -"0x7ff78d555227: enum2$ > > > >::and_then (core\\src\\option.rs:1023:0)", -"0x7ff78d555227: std::sys::thread_local::native::lazy::Storage >::initialize,parking_lot_core::parking_lot::ThreadData (*)()> (thread_local\\native\\lazy.rs:64:0)", -"0x7ff78d4f90a6: std::sys::thread_local::native::lazy::Storage >::get_or_init (thread_local\\native\\lazy.rs:56:0)", -"0x7ff78d4f90a6: parking_lot_core::parking_lot::with_thread_data::THREAD_DATA::constant$0::closure$0 (thread_local\\native\\mod.rs:94:0)", -"0x7ff78d4f90a6: core::ops::function::FnOnce::call_once (src\\ops\\function.rs:250:0)", -"0x7ff78d4f90a6: std::thread::local::LocalKey::try_with (src\\thread\\local.rs:310:0)", -"0x7ff78d4f90a6: parking_lot_core::parking_lot::with_thread_data (parking_lot_core-0.9.10\\src\\parking_lot.rs:203:0)", -"0x7ff78d4f90a6: parking_lot_core::parking_lot::park (parking_lot_core-0.9.10\\src\\parking_lot.rs:600:0)", -"0x7ff78d4f90a6: parking_lot::condvar::Condvar::wait_until_internal (parking_lot-0.12.3\\src\\condvar.rs:334:0)", -"0x7ff78d4dd47e: parking_lot::condvar::Condvar::wait (parking_lot-0.12.3\\src\\condvar.rs:256:0)", -"0x7ff78d4dd47e: tokio::loom::std::parking_lot::Condvar::wait (loom\\std\\parking_lot.rs:157:0)", -"0x7ff78d4dd47e: tokio::runtime::scheduler::multi_thread::park::Inner::park_condvar (scheduler\\multi_thread\\park.rs:158:0)", -"0x7ff78d4dd47e: tokio::runtime::scheduler::multi_thread::park::Inner::park (scheduler\\multi_thread\\park.rs:129:0)", -"0x7ff78d4dd47e: tokio::runtime::scheduler::multi_thread::park::Parker::park (scheduler\\multi_thread\\park.rs:70:0)", -"0x7ff78d4da9fb: tokio::runtime::scheduler::multi_thread::worker::Context::park_timeout (scheduler\\multi_thread\\worker.rs:766:0)", -"0x7ff78d4d965a: tokio::runtime::scheduler::multi_thread::worker::Context::park (scheduler\\multi_thread\\worker.rs:734:0)", -"0x7ff78d4d965a: tokio::runtime::scheduler::multi_thread::worker::Context::run (scheduler\\multi_thread\\worker.rs:560:0)", -"0x7ff78d4e10d9: tokio::runtime::scheduler::multi_thread::worker::run::closure$0::closure$0 (scheduler\\multi_thread\\worker.rs:507:0)", -"0x7ff78d4e10d9: tokio::runtime::context::scoped::Scoped >::set,tokio::runtime::scheduler::multi_thread::worker::run::closure$0::closure_env$0,tuple$<> > (runtime\\context\\scoped.rs:40:0)", -"0x7ff78d4dcef4: tokio::runtime::context::set_scheduler::closure$0 (src\\runtime\\context.rs:180:0)", -"0x7ff78d4dcef4: std::thread::local::LocalKey::try_with (src\\thread\\local.rs:311:0)", -"0x7ff78d4dcef4: std::thread::local::LocalKey::with (src\\thread\\local.rs:275:0)", -"0x7ff78d4dcef4: tokio::runtime::context::set_scheduler (src\\runtime\\context.rs:180:0)", -"0x7ff78d4dcef4: tokio::runtime::scheduler::multi_thread::worker::run::closure$0 (scheduler\\multi_thread\\worker.rs:502:0)", -"0x7ff78d4dcef4: tokio::runtime::context::runtime::enter_runtime > (runtime\\context\\runtime.rs:65:0)", -"0x7ff78d195a64: wfl::diagnostics::DiagnosticReporter::report_diagnostic (src\\diagnostics\\mod.rs:163:0)", -"0x7ff78d50a544: std::sys::pal::windows::args::parse_lp_cmd_line (pal\\windows\\args.rs:88:0)", -"0x7ff78d50a544: std::sys::pal::windows::args::args (pal\\windows\\args.rs:26:0)", -"0x7ff78d50a544: std::env::args_os (std\\src\\env.rs:863:0)", -"0x7ff78d4f9ad8: alloc::alloc::exchange_malloc (alloc\\src\\alloc.rs:350:0)", -"0x7ff78d4f9ad8: alloc::boxed::impl$0::new (alloc\\src\\boxed.rs:261:0)", -"0x7ff78d4f9ad8: parking_lot_core::parking_lot::HashTable::new (parking_lot_core-0.9.10\\src\\parking_lot.rs:81:0)", -"0x7ff78d166b53: std::fs::read_to_string (std\\src\\fs.rs:320:0)", -"0x7ff78d166b53: wfl::config::load_config (wfl\\src\\config.rs:337:0)", -"0x7ff78d147b93: alloc::string::impl$49::from (alloc\\src\\string.rs:2967:0)", -"0x7ff78d147b93: core::convert::impl$3::into (src\\convert\\mod.rs:761:0)", -"0x7ff78d147b93: wfl::diagnostics::DiagnosticReporter::add_file,ref$ > (src\\diagnostics\\mod.rs:151:0)", -"0x7ff78d108b33: wfl::main::async_block$0 (wfl\\src\\main.rs:323:0)", -"0x7ff78d4fa4de: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d4fa4de: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d4fa4de: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d4fa4de: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d4fa4de: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d4fa4de: alloc::slice::impl$0::to_vec_in::impl$1::to_vec (alloc\\src\\slice.rs:446:0)", -"0x7ff78d4fa4de: alloc::slice::impl$0::to_vec_in (alloc\\src\\slice.rs:399:0)", -"0x7ff78d4fa4de: alloc::slice::impl$0::to_vec (alloc\\src\\slice.rs:375:0)", -"0x7ff78d4fa4de: alloc::slice::impl$9::to_owned (alloc\\src\\slice.rs:841:0)", -"0x7ff78d4fa4de: alloc::str::impl$4::to_owned (alloc\\src\\str.rs:211:0)", -"0x7ff78d4fa4de: alloc::string::impl$47::from (alloc\\src\\string.rs:2943:0)", -"0x7ff78d4fa4de: core::convert::impl$3::into (src\\convert\\mod.rs:761:0)", -"0x7ff78d4fa4de: codespan_reporting::term::config::Chars::box_drawing (src\\term\\config.rs:269:0)", -"0x7ff78d4fa4de: codespan_reporting::term::config::impl$3::default (src\\term\\config.rs:261:0)", -"0x7ff78d4fa4de: codespan_reporting::term::config::impl$0::default (src\\term\\config.rs:40:0)", -"0x7ff78d195c54: wfl::diagnostics::DiagnosticReporter::report_diagnostic (src\\diagnostics\\mod.rs:173:0)", -"0x7ff78d5083a1: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d5083a1: alloc::raw_vec::RawVecInner::grow_one (src\\raw_vec\\mod.rs:571:0)", -"0x7ff78d5083a1: alloc::raw_vec::RawVec::grow_one (src\\raw_vec\\mod.rs:340:0)", -"0x7ff78d50a70a: alloc::vec::Vec::push (src\\vec\\mod.rs:2448:0)", -"0x7ff78d50a70a: std::sys::pal::windows::args::parse_lp_cmd_line (pal\\windows\\args.rs:146:0)", -"0x7ff78d50a70a: std::sys::pal::windows::args::args (pal\\windows\\args.rs:26:0)", -"0x7ff78d50a70a: std::env::args_os (std\\src\\env.rs:863:0)", -"0x7ff78d51bc42: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d51bc42: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d51bc42: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d51bc42: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d51bc42: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d51bc42: alloc::vec::Vec::with_capacity (src\\vec\\mod.rs:495:0)", -"0x7ff78d51bc42: std::sys::pal::windows::to_u16s::inner (pal\\windows\\mod.rs:177:0)", -"0x7ff78d51d46b: std::sys::pal::windows::to_u16s (pal\\windows\\mod.rs:189:0)", -"0x7ff78d51d46b: std::sys::path::windows::maybe_verbatim (sys\\path\\windows.rs:221:0)", -"0x7ff78d51d46b: std::sys::fs::windows::File::open (sys\\fs\\windows.rs:300:0)", -"0x7ff78d51d46b: std::sys::fs::windows::metadata (sys\\fs\\windows.rs:1432:0)", -"0x7ff78d51d1f2: std::sys::fs::windows::stat (sys\\fs\\windows.rs:1394:0)", -"0x7ff78d166b38: std::fs::metadata (std\\src\\fs.rs:2412:0)", -"0x7ff78d166b38: std::path::Path::exists (std\\src\\path.rs:3020:0)", -"0x7ff78d166b38: wfl::config::load_config (wfl\\src\\config.rs:336:0)", -"0x7ff78d50b8d9: std::sys::pal::windows::to_u16s (pal\\windows\\mod.rs:189:0)", -"0x7ff78d50b8d9: std::sys::path::windows::maybe_verbatim (sys\\path\\windows.rs:221:0)", -"0x7ff78d50b8d9: std::sys::fs::windows::File::open (sys\\fs\\windows.rs:300:0)", -"0x7ff78d50b8d9: std::fs::OpenOptions::_open (std\\src\\fs.rs:1594:0)", -"0x7ff78d50b11e: std::fs::OpenOptions::open (std\\src\\fs.rs:1590:0)", -"0x7ff78d50b11e: std::fs::File::open (std\\src\\fs.rs:384:0)", -"0x7ff78d50b11e: std::fs::read_to_string::inner (std\\src\\fs.rs:313:0)", -"0x7ff78d194aa4: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d194aa4: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d194aa4: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d194aa4: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d194aa4: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d194aa4: alloc::slice::impl$0::to_vec_in::impl$1::to_vec (alloc\\src\\slice.rs:446:0)", -"0x7ff78d194aa4: alloc::slice::impl$0::to_vec_in (alloc\\src\\slice.rs:399:0)", -"0x7ff78d194aa4: alloc::slice::impl$0::to_vec (alloc\\src\\slice.rs:375:0)", -"0x7ff78d194aa4: alloc::slice::impl$9::to_owned (alloc\\src\\slice.rs:841:0)", -"0x7ff78d194aa4: alloc::str::impl$4::to_owned (alloc\\src\\str.rs:211:0)", -"0x7ff78d194aa4: alloc::string::impl$47::from (alloc\\src\\string.rs:2943:0)", -"0x7ff78d194aa4: core::convert::impl$3::into (src\\convert\\mod.rs:761:0)", -"0x7ff78d194aa4: wfl::diagnostics::WflDiagnostic::with_primary_label > (src\\diagnostics\\mod.rs:107:0)", -"0x7ff78d19673f: wfl::diagnostics::DiagnosticReporter::convert_parse_error (src\\diagnostics\\mod.rs:213:0)", -"0x7ff78d50824c: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d50824c: alloc::raw_vec::RawVecInner::grow_one (src\\raw_vec\\mod.rs:571:0)", -"0x7ff78d50824c: alloc::raw_vec::RawVec::grow_one,void (*)(ptr_mut$)>,alloc::alloc::Global> (src\\raw_vec\\mod.rs:340:0)", -"0x7ff78d525637: alloc::vec::Vec::push (src\\vec\\mod.rs:2448:0)", -"0x7ff78d525637: std::sys::thread_local::destructors::list::register (thread_local\\destructors\\list.rs:17:0)", -"0x7ff78d509257: std::sys::thread_local::native::eager::Storage::initialize (thread_local\\native\\eager.rs:47:0)", -"0x7ff78d509257: std::sys::thread_local::native::eager::Storage::get (thread_local\\native\\eager.rs:36:0)", -"0x7ff78d509257: std::thread::spawnhook::SPAWN_HOOKS::constant$0::closure$0 (thread_local\\native\\mod.rs:67:0)", -"0x7ff78d509257: core::ops::function::FnOnce::call_once (src\\ops\\function.rs:250:0)", -"0x7ff78d509257: std::thread::local::LocalKey::initialize_with (src\\thread\\local.rs:333:0)", -"0x7ff78d509257: std::thread::local::LocalKey::set (src\\thread\\local.rs:372:0)", -"0x7ff78d509257: std::thread::spawnhook::ChildSpawnHooks::run (src\\thread\\spawnhook.rs:148:0)", -"0x7ff78d4db56f: std::thread::impl$0::spawn_unchecked_::closure$1::closure$0::closure$0 (src\\thread\\mod.rs:558:0)", -"0x7ff78d4db56f: std::sys::backtrace::__rust_begin_short_backtrace >,tuple$<> > (src\\sys\\backtrace.rs:152:0)", -"0x7ff78d4c601c: core::ops::function::FnOnce::call_once >,tuple$<> > (src\\ops\\function.rs:250:0)", -"0x7ff78d51b5fd: alloc::boxed::impl$28::call_once (alloc\\src\\boxed.rs:1966:0)", -"0x7ff78d51b5fd: std::sys::pal::windows::thread::impl$0::new::thread_start (pal\\windows\\thread.rs:56:0)", -"0x7ff78d19593a: wfl::diagnostics::DiagnosticReporter::report_diagnostic (src\\diagnostics\\mod.rs:164:0)", -"0x7ff78d1c55d6: codespan_reporting::term::views::RichDiagnostic::render > (src\\term\\views.rs:125:0)", -"0x7ff78d166e47: std::fs::metadata (std\\src\\fs.rs:2412:0)", -"0x7ff78d166e47: std::path::impl$45::deref (std\\src\\path.rs:3020:0)", -"0x7ff78d166e47: wfl::config::load_config (wfl\\src\\config.rs:361:0)", -"0x7ff78d509ec2: std::sys::pal::windows::to_u16s (pal\\windows\\mod.rs:189:0)", -"0x7ff78d509ec2: std::sys::pal::windows::os::getenv (pal\\windows\\os.rs:294:0)", -"0x7ff78d509ec2: std::env::_var_os (std\\src\\env.rs:262:0)", -"0x7ff78d509d69: std::env::var_os (std\\src\\env.rs:258:0)", -"0x7ff78d509d69: std::env::_var (std\\src\\env.rs:225:0)", -"0x7ff78d16475b: std::env::var (std\\src\\env.rs:221:0)", -"0x7ff78d16475b: wfl::config::get_global_config_path (wfl\\src\\config.rs:11:0)", -"0x7ff78d166b23: wfl::config::load_config (wfl\\src\\config.rs:333:0)", -"0x7ff78d19a16e: wfl::parser::Parser::new (src\\parser\\mod.rs:16:0)", -"0x7ff78d108f3a: wfl::main::async_block$0 (wfl\\src\\main.rs:485:0)", -"0x7ff78d50a517: alloc::vec::Vec::push (src\\vec\\mod.rs:2448:0)", -"0x7ff78d50a517: std::sys::pal::windows::args::parse_lp_cmd_line (pal\\windows\\args.rs:83:0)", -"0x7ff78d50a517: std::sys::pal::windows::args::args (pal\\windows\\args.rs:26:0)", -"0x7ff78d50a517: std::env::args_os (std\\src\\env.rs:863:0)", -"0x7ff78d1dfacf: alloc::vec::impl$11::clone (src\\vec\\mod.rs:3312:0)", -"0x7ff78d16e6c1: wfl::lexer::intern_string (src\\lexer\\mod.rs:18:0)", -"0x7ff78d19303f: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d19303f: alloc::raw_vec::RawVecInner::grow_one (src\\raw_vec\\mod.rs:571:0)", -"0x7ff78d19303f: alloc::raw_vec::RawVec,alloc::alloc::Global>::grow_one,alloc::alloc::Global> (src\\raw_vec\\mod.rs:340:0)", -"0x7ff78d1c56bb: codespan_reporting::term::views::RichDiagnostic::render > (src\\term\\views.rs:122:0)", -"0x7ff78d16f3f4: wfl::lexer::lex_wfl_with_positions (src\\lexer\\mod.rs:143:0)", -"0x7ff78d196706: wfl::diagnostics::DiagnosticReporter::convert_parse_error (src\\diagnostics\\mod.rs:213:0)", -"0x7ff78d1c5602: codespan_reporting::term::views::RichDiagnostic::render > (src\\term\\views.rs:125:0)", -"0x7ff78d1927ff: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d1927ff: alloc::raw_vec::RawVecInner::grow_one (src\\raw_vec\\mod.rs:571:0)", -"0x7ff78d1927ff: alloc::raw_vec::RawVec::grow_one (src\\raw_vec\\mod.rs:340:0)", -"0x7ff78d1c5a3b: codespan_reporting::term::views::RichDiagnostic::render > (src\\term\\views.rs:170:0)", -"0x7ff78d12efc5: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d12efc5: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d12efc5: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d12efc5: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d12efc5: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d12efc5: alloc::vec::Vec::with_capacity (src\\vec\\mod.rs:495:0)", -"0x7ff78d12efc5: alloc::vec::spec_from_iter_nested::impl$0::from_iter (src\\vec\\spec_from_iter_nested.rs:31:0)", -"0x7ff78d12efc5: alloc::vec::spec_from_iter::impl$0::from_iter,core::iter::adapters::map::Map,codespan_reporting::files::line_starts::closure_env$0> > > (src\\vec\\spec_from_iter.rs:34:0)", -"0x7ff78d149f50: alloc::vec::impl$15::from_iter (src\\vec\\mod.rs:3438:0)", -"0x7ff78d149f50: core::iter::traits::iterator::Iterator::collect (iter\\traits\\iterator.rs:1985:0)", -"0x7ff78d149f50: codespan_reporting::files::SimpleFile::new (codespan-reporting-0.11.1\\src\\files.rs:282:0)", -"0x7ff78d149f50: codespan_reporting::files::SimpleFiles::add (codespan-reporting-0.11.1\\src\\files.rs:373:0)", -"0x7ff78d1966db: wfl::diagnostics::DiagnosticReporter::convert_parse_error (src\\diagnostics\\mod.rs:206:0)", -"0x7ff78d1fec96: wfl::lexer::token::impl$2::lex::goto114_ctx113_x (src\\lexer\\token.rs:3:0)", -"0x7ff78d16e76d: wfl::lexer::intern_string (src\\lexer\\mod.rs:16:0)", -"0x7ff78d16f6ce: wfl::lexer::lex_wfl_with_positions (src\\lexer\\mod.rs:169:0)", -"0x7ff78d52d695: alloc::alloc::impl$1::allocate (alloc\\src\\alloc.rs:249:0)", -"0x7ff78d52d695: alloc::raw_vec::RawVecInner::try_allocate_in (src\\raw_vec\\mod.rs:476:0)", -"0x7ff78d52d695: alloc::raw_vec::RawVecInner::with_capacity_in (src\\raw_vec\\mod.rs:422:0)", -"0x7ff78d52d695: alloc::raw_vec::RawVec::with_capacity_in (src\\raw_vec\\mod.rs:190:0)", -"0x7ff78d52d695: alloc::vec::Vec::with_capacity_in (src\\vec\\mod.rs:815:0)", -"0x7ff78d52d695: alloc::vec::Vec::with_capacity (src\\vec\\mod.rs:495:0)", -"0x7ff78d52d695: alloc::string::String::with_capacity (alloc\\src\\string.rs:488:0)", -"0x7ff78d52d695: alloc::fmt::format::format_inner (alloc\\src\\fmt.rs:647:0)", -"0x7ff78d19c5e9: wfl::parser::Parser::expect_token (src\\parser\\mod.rs:286:0)", -"0x7ff78d1a4744: wfl::parser::Parser::parse_open_file_statement (src\\parser\\mod.rs:1668:0)", -"0x7ff78d19a2f5: wfl::parser::Parser::parse (src\\parser\\mod.rs:28:0)", -"0x7ff78d19297b: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d19297b: alloc::raw_vec::RawVecInner::grow_one (src\\raw_vec\\mod.rs:571:0)", -"0x7ff78d19297b: alloc::raw_vec::RawVec::grow_one (src\\raw_vec\\mod.rs:340:0)", -"0x7ff78d16f608: wfl::lexer::lex_wfl_with_positions (src\\lexer\\mod.rs:149:0)", -"0x7ff78d147ba3: alloc::string::impl$49::from (alloc\\src\\string.rs:2967:0)", -"0x7ff78d147ba3: core::convert::impl$3::into (src\\convert\\mod.rs:761:0)", -"0x7ff78d147ba3: wfl::diagnostics::DiagnosticReporter::add_file,ref$ > (src\\diagnostics\\mod.rs:151:0)", -"0x7ff78d16edf1: wfl::lexer::lex_wfl_with_positions (src\\lexer\\mod.rs:87:0)", -"0x7ff78d1a46df: wfl::parser::Parser::parse_open_file_statement (src\\parser\\mod.rs:1650:0)", -"0x7ff78d1957fd: wfl::diagnostics::DiagnosticReporter::report_diagnostic (src\\diagnostics\\mod.rs:159:0)", -"0x7ff78d194ae2: alloc::vec::Vec,alloc::alloc::Global>::push (src\\vec\\mod.rs:2448:0)", -"0x7ff78d194ae2: wfl::diagnostics::WflDiagnostic::with_primary_label > (src\\diagnostics\\mod.rs:107:0)", -"0x7ff78d5082fc: alloc::raw_vec::RawVecInner::grow_amortized (src\\raw_vec\\mod.rs:664:0)", -"0x7ff78d5082fc: alloc::raw_vec::RawVecInner::grow_one (src\\raw_vec\\mod.rs:571:0)", -"0x7ff78d5082fc: alloc::raw_vec::RawVec::grow_one (src\\raw_vec\\mod.rs:340:0)", -"0x7ff78d50a557: alloc::vec::Vec::push (src\\vec\\mod.rs:2448:0)", -"0x7ff78d50a557: std::sys::pal::windows::args::parse_lp_cmd_line (pal\\windows\\args.rs:88:0)", -"0x7ff78d50a557: std::sys::pal::windows::args::args (pal\\windows\\args.rs:26:0)", -"0x7ff78d50a557: std::env::args_os (std\\src\\env.rs:863:0)", -"0x7ff78d16ec0a: alloc::raw_vec::RawVecInner::reserve (src\\raw_vec\\mod.rs:563:0)", -"0x7ff78d16ec0a: alloc::raw_vec::RawVec::reserve (src\\raw_vec\\mod.rs:331:0)", -"0x7ff78d16ec0a: alloc::vec::Vec::reserve (src\\vec\\mod.rs:1297:0)", -"0x7ff78d16ec0a: alloc::vec::Vec::append_elements (src\\vec\\mod.rs:2592:0)", -"0x7ff78d16ec0a: alloc::vec::spec_extend::impl$4::spec_extend (src\\vec\\spec_extend.rs:61:0)", -"0x7ff78d16ec0a: alloc::vec::Vec::extend_from_slice (src\\vec\\mod.rs:3059:0)", -"0x7ff78d16ec0a: alloc::string::String::push_str (alloc\\src\\string.rs:1112:0)", -"0x7ff78d16ec0a: alloc::str::impl$5::replace (alloc\\src\\str.rs:290:0)", -"0x7ff78d16ec0a: wfl::lexer::normalize_line_endings (src\\lexer\\mod.rs:24:0)", -"0x7ff78d16ed86: wfl::lexer::lex_wfl_with_positions (src\\lexer\\mod.rs:77:0)", -"0x7ff78d51b6ce: std::sys::pal::windows::to_u16s (pal\\windows\\mod.rs:189:0)", -"0x7ff78d51b6ce: std::sys::pal::windows::thread::Thread::set_name (pal\\windows\\thread.rs:63:0)", -"0x7ff78d4c5fc4: core::ops::function::FnOnce::call_once >,tuple$<> > (src\\ops\\function.rs:250:0)", -"0x7ff78d12f24b: alloc::vec::spec_from_iter_nested::impl$0::from_iter (src\\vec\\spec_from_iter_nested.rs:31:0)", -"0x7ff78d12f24b: alloc::vec::spec_from_iter::impl$0::from_iter (src\\vec\\spec_from_iter.rs:34:0)", -"0x7ff78d107e9c: alloc::vec::impl$15::from_iter (src\\vec\\mod.rs:3438:0)", -"0x7ff78d107e9c: core::iter::traits::iterator::Iterator::collect (iter\\traits\\iterator.rs:1985:0)", -"0x7ff78d107e9c: wfl::main::async_block$0 (wfl\\src\\main.rs:60:0)" -] -} \ No newline at end of file diff --git a/inheritance_and_interfaces.md b/inheritance_and_interfaces.md deleted file mode 100644 index 81302b04..00000000 --- a/inheritance_and_interfaces.md +++ /dev/null @@ -1,692 +0,0 @@ -# Inheritance and Interface Implementation in WFL - -This document provides a detailed explanation of how inheritance and interfaces will work together in the WFL container implementation. - -## 1. Inheritance and Interface Architecture - -```mermaid -graph TD - A[Container Definition] --> B[Properties] - A --> C[Methods] - A --> D[Events] - - E[Parent Container] --> F[Child Container] - G[Interface] --> H[Implementing Container] - - I[Method Resolution] --> J[Property Resolution] - K[Interface Validation] --> L[Type Checking] -``` - -## 2. Container Inheritance Model - -Inheritance in WFL containers follows a single-inheritance model, where a container can extend exactly one parent container: - -```wfl -create container Vehicle: - property make as text - property model as text - - define action describe: - display make with " " with model - end action -end container - -create container Car extends Vehicle: - property doors as number defaults to 4 - - // Override parent method - define action describe: - parent describe // Call parent method - display "with " with doors with " doors" - end action -end container -``` - -In the AST, inheritance is represented by the `extends` field in the `ContainerDefinition` structure: - -```rust -ContainerDefinition { - name: String, - extends: Option, // Name of parent container - // Other fields... -} -``` - -## 3. Interface Definition and Implementation - -Interfaces define a contract that containers must fulfill: - -```wfl -create interface Drawable: - requires action draw - requires action resize with width and height -end interface - -create container Circle implements Drawable: - property radius as number - - define action draw: // Required by Drawable - display "Drawing circle with radius " with radius - end action - - define action resize with width and height: // Required by Drawable - set radius to minimum of width and height divided by 2 - end action -end container -``` - -In the AST, interfaces are represented by the `InterfaceDefinition` structure, and interface implementation is represented by the `implements` field in the `ContainerDefinition` structure: - -```rust -InterfaceDefinition { - name: String, - required_actions: Vec, - line: usize, - column: usize, -} - -ContainerDefinition { - // Other fields... - implements: Vec, // Names of implemented interfaces -} -``` - -## 4. Method Resolution Order - -When a method is called on a container instance, the interpreter follows this resolution order: - -1. Look for the method in the container's own methods -2. If not found, look in the parent container (and so on up the inheritance chain) -3. If still not found, check if any implemented interfaces define the method - -```rust -fn resolve_method( - &self, - container: &ContainerValue, - method_name: &str, -) -> Option> { - // Check method cache first - if let Some((_, method)) = container.method_cache.borrow().get(method_name) { - return Some(method.clone()); - } - - // Check own methods - if let Some(method) = container.methods.get(method_name) { - return Some(method.clone()); - } - - // Check parent container - if let Some(parent_weak) = &container.extends { - if let Some(parent) = parent_weak.upgrade() { - if let Some(method) = self.resolve_method(&parent, method_name) { - // Cache the result - container.method_cache.borrow_mut().insert( - method_name.to_string(), - (Rc::downgrade(&parent), method.clone()) - ); - return Some(method); - } - } - } - - None -} -``` - -## 5. Property Resolution Order - -Similarly, property resolution follows the inheritance chain: - -```rust -fn resolve_property( - &self, - container: &ContainerValue, - property_name: &str, -) -> Option { - // Check property cache first - if let Some((_, property)) = container.property_cache.borrow().get(property_name) { - return Some(property.clone()); - } - - // Check own properties - if let Some(property) = container.properties.get(property_name) { - return Some(property.clone()); - } - - // Check parent container - if let Some(parent_weak) = &container.extends { - if let Some(parent) = parent_weak.upgrade() { - if let Some(property) = self.resolve_property(&parent, property_name) { - // Cache the result - container.property_cache.borrow_mut().insert( - property_name.to_string(), - (Rc::downgrade(&parent), property.clone()) - ); - return Some(property); - } - } - } - - None -} -``` - -## 6. Parent Method Calls - -WFL allows child containers to call methods from their parent containers using the `parent` keyword: - -```wfl -define action describe: - parent describe // Call parent method - display "with " with doors with " doors" -end action -``` - -To implement this, we need to handle the `parent` keyword in method calls: - -```rust -async fn execute_parent_method_call( - &self, - method_name: &str, - arguments: &[Argument], - env: Rc>, -) -> Result { - // Get the current container instance from the environment - let this_value = match env.borrow().get("this") { - Some(Value::ContainerInstance(instance)) => Value::ContainerInstance(instance), - _ => return Err(RuntimeError::new( - "Cannot call parent method outside of container method".to_string(), - /* line */, - /* column */, - )), - }; - - match this_value { - Value::ContainerInstance(instance) => { - let instance_ref = instance.borrow(); - - // Get the container definition - let container = match instance_ref.container.upgrade() { - Some(c) => c, - None => return Err(RuntimeError::new( - "Container no longer exists".to_string(), - /* line */, - /* column */, - )), - }; - - // Get the parent container - let parent = match &container.extends { - Some(parent_weak) => match parent_weak.upgrade() { - Some(p) => p, - None => return Err(RuntimeError::new( - "Parent container no longer exists".to_string(), - /* line */, - /* column */, - )), - }, - None => return Err(RuntimeError::new( - "Container has no parent".to_string(), - /* line */, - /* column */, - )), - }; - - // Look up the method in the parent container - let method = match parent.methods.get(method_name) { - Some(m) => m.clone(), - None => return Err(RuntimeError::new( - format!("Method '{}' not found in parent container", method_name), - /* line */, - /* column */, - )), - }; - - // Evaluate arguments - let mut arg_values = Vec::new(); - for arg in arguments { - let value = self.evaluate_expression(&arg.value, Rc::clone(&env)).await?; - arg_values.push(value); - } - - // Call the method - self.call_function(&method, arg_values, /* line */, /* column */).await - }, - _ => Err(RuntimeError::new( - "Cannot call parent method outside of container method".to_string(), - /* line */, - /* column */, - )), - } -} -``` - -## 7. Interface Validation - -When a container implements an interface, we need to validate that it provides all the required methods: - -```rust -fn validate_interface_implementation( - &self, - container: &ContainerValue, - interface: &InterfaceValue, -) -> Result<(), RuntimeError> { - for (method_name, signature) in &interface.required_actions { - // Check if the container has the method - let method = match self.resolve_method(container, method_name) { - Some(m) => m, - None => return Err(RuntimeError::new( - format!( - "Container '{}' does not implement required method '{}' from interface '{}'", - container.name, method_name, interface.name - ), - /* line */, - /* column */, - )), - }; - - // Check if the method signature matches - if method.params.len() != signature.parameters.len() { - return Err(RuntimeError::new( - format!( - "Method '{}' in container '{}' has wrong number of parameters for interface '{}'", - method_name, container.name, interface.name - ), - /* line */, - /* column */, - )); - } - - // TODO: Check parameter types and return type - } - - Ok(()) -} -``` - -## 8. Multiple Interface Implementation - -A container can implement multiple interfaces: - -```wfl -create container MultiButton extends UIElement implements Clickable, Draggable, Resizable: - // Implementation of all required methods from all interfaces -end container -``` - -When validating interface implementation, we need to check all implemented interfaces: - -```rust -async fn execute_container_definition( - &self, - name: &str, - extends: Option<&str>, - implements: &[String], - // Other parameters... -) -> Result { - // Create container value - let container = Rc::new(ContainerValue { - name: name.to_string(), - extends: None, // Will be set later if extends is Some - implements: Vec::new(), // Will be populated later - // Other fields... - }); - - // Set parent container if extends is Some - if let Some(parent_name) = extends { - let parent_value = match env.borrow().get(parent_name) { - Some(Value::Container(parent)) => parent, - _ => return Err(RuntimeError::new( - format!("Parent container '{}' not found", parent_name), - /* line */, - /* column */, - )), - }; - - container.extends = Some(Rc::downgrade(&parent_value)); - } - - // Set implemented interfaces - for interface_name in implements { - let interface_value = match env.borrow().get(interface_name) { - Some(Value::Interface(interface)) => interface, - _ => return Err(RuntimeError::new( - format!("Interface '{}' not found", interface_name), - /* line */, - /* column */, - )), - }; - - container.implements.push(Rc::downgrade(&interface_value)); - - // Validate interface implementation - self.validate_interface_implementation(&container, &interface_value)?; - } - - // Register container in environment - env.borrow_mut().define(name, Value::Container(container)); - - Ok(Value::Null) -} -``` - -## 9. Polymorphism - -Interfaces enable polymorphism, allowing different container types to be used interchangeably: - -```wfl -create list shapes: - add new Circle with radius 5 - add new Rectangle with width 10 and height 20 -end list - -for each shape in shapes: - shape draw // Works for both Circle and Rectangle -end for -``` - -To support this, we need to handle method calls on container instances that implement interfaces: - -```rust -async fn execute_method_call( - &self, - object: &Expression, - method_name: &str, - arguments: &[Argument], - env: Rc>, -) -> Result { - // Evaluate object expression - let object_value = self.evaluate_expression(object, Rc::clone(&env)).await?; - - match object_value { - Value::ContainerInstance(instance) => { - let instance_ref = instance.borrow(); - - // Get the container definition - let container = match instance_ref.container.upgrade() { - Some(c) => c, - None => return Err(RuntimeError::new( - "Container no longer exists".to_string(), - /* line */, - /* column */, - )), - }; - - // Resolve the method - let method = match self.resolve_method(&container, method_name) { - Some(m) => m, - None => return Err(RuntimeError::new( - format!("Method '{}' not found on container '{}'", method_name, container.name), - /* line */, - /* column */, - )), - }; - - // Evaluate arguments - let mut arg_values = Vec::new(); - for arg in arguments { - let value = self.evaluate_expression(&arg.value, Rc::clone(&env)).await?; - arg_values.push(value); - } - - // Create a new environment for the method - let method_env = Environment::new(&env); - - // Add 'this' to the environment - method_env.borrow_mut().define("this", Value::ContainerInstance(instance.clone())); - - // Call the method - self.call_function(&method, arg_values, /* line */, /* column */).await - }, - _ => Err(RuntimeError::new( - format!("Cannot call method '{}' on non-container value", method_name), - /* line */, - /* column */, - )), - } -} -``` - -## 10. Memory Management for Inheritance and Interfaces - -To avoid memory leaks and reference cycles in the inheritance and interface system, we'll use several strategies: - -### 10.1 Weak References for Parent Containers - -```rust -pub struct ContainerValue { - // Other fields... - pub extends: Option>, // Weak reference to avoid cycles - pub implements: Vec>, // Weak references to avoid cycles -} -``` - -### 10.2 Method and Property Caching - -To improve performance, we'll cache resolved methods and properties: - -```rust -pub struct ContainerValue { - // Other fields... - pub method_cache: RefCell, Rc)>>, - pub property_cache: RefCell, PropertyDefinition)>>, -} -``` - -### 10.3 Cache Invalidation - -When a container is modified, we need to invalidate its caches: - -```rust -fn invalidate_caches(&self, container: &ContainerValue) { - container.method_cache.borrow_mut().clear(); - container.property_cache.borrow_mut().clear(); - - // Also invalidate caches of child containers - // This would require maintaining a list of weak references to child containers -} -``` - -## 11. Type Checking for Inheritance and Interfaces - -The type checker needs to understand container inheritance and interface implementation: - -```rust -fn check_method_call( - &mut self, - object_type: &Type, - method_name: &str, - arguments: &[Argument], -) -> Result { - match object_type { - Type::ContainerInstance(container_name) => { - // Look up container - let container = self.lookup_container(container_name)?; - - // Look up method - let method = self.lookup_method(&container, method_name)?; - - // Check arguments - self.check_arguments(&method, arguments)?; - - // Return method return type - Ok(method.return_type) - }, - Type::Interface(interface_name) => { - // Look up interface - let interface = self.lookup_interface(interface_name)?; - - // Look up method in interface - let method = self.lookup_interface_method(&interface, method_name)?; - - // Check arguments - self.check_arguments(&method, arguments)?; - - // Return method return type - Ok(method.return_type) - }, - _ => Err(TypeError::new( - format!("Cannot call method '{}' on non-container type {:?}", method_name, object_type), - /* line */, - /* column */, - )), - } -} -``` - -## 12. Interface Implementation Challenges and Solutions - -### 12.1 Challenge: Method Signature Compatibility - -When implementing an interface, the method signatures must be compatible with the interface requirements. - -**Solution**: Implement a signature compatibility checker: - -```rust -fn check_signature_compatibility( - &self, - container_method: &FunctionValue, - interface_signature: &ActionSignature, -) -> Result<(), RuntimeError> { - // Check parameter count - if container_method.params.len() != interface_signature.parameters.len() { - return Err(RuntimeError::new( - "Parameter count mismatch".to_string(), - /* line */, - /* column */, - )); - } - - // Check parameter types (if available) - // Check return type (if available) - - Ok(()) -} -``` - -### 12.2 Challenge: Interface Inheritance - -Interfaces might inherit from other interfaces: - -```wfl -create interface Drawable: - requires action draw -end interface - -create interface AnimatedDrawable extends Drawable: - requires action animate - // Inherits 'draw' requirement from Drawable -end interface -``` - -**Solution**: Implement interface inheritance: - -```rust -fn resolve_interface_method( - &self, - interface: &InterfaceValue, - method_name: &str, -) -> Option { - // Check own methods - if let Some(signature) = interface.required_actions.get(method_name) { - return Some(signature.clone()); - } - - // Check parent interfaces - for parent_weak in &interface.extends { - if let Some(parent) = parent_weak.upgrade() { - if let Some(signature) = self.resolve_interface_method(&parent, method_name) { - return Some(signature); - } - } - } - - None -} -``` - -### 12.3 Challenge: Diamond Problem - -With multiple interface implementation, we might encounter the diamond problem: - -```wfl -create interface A: - requires action foo -end interface - -create interface B extends A: - requires action bar -end interface - -create interface C extends A: - requires action baz -end interface - -create container D implements B, C: - // Must implement foo, bar, and baz - // But foo is required by both B and C -end container -``` - -**Solution**: Implement a method resolution order that handles the diamond problem: - -```rust -fn validate_multiple_interfaces( - &self, - container: &ContainerValue, - interfaces: &[Weak], -) -> Result<(), RuntimeError> { - // Collect all required methods from all interfaces - let mut required_methods = HashMap::new(); - - for interface_weak in interfaces { - if let Some(interface) = interface_weak.upgrade() { - self.collect_required_methods(&interface, &mut required_methods)?; - } - } - - // Check that container implements all required methods - for (method_name, signature) in required_methods { - // Check if container has the method - let method = match self.resolve_method(container, &method_name) { - Some(m) => m, - None => return Err(RuntimeError::new( - format!("Container '{}' does not implement required method '{}'", container.name, method_name), - /* line */, - /* column */, - )), - }; - - // Check signature compatibility - self.check_signature_compatibility(&method, &signature)?; - } - - Ok(()) -} - -fn collect_required_methods( - &self, - interface: &InterfaceValue, - required_methods: &mut HashMap, -) -> Result<(), RuntimeError> { - // Add own required methods - for (name, signature) in &interface.required_actions { - required_methods.insert(name.clone(), signature.clone()); - } - - // Add required methods from parent interfaces - for parent_weak in &interface.extends { - if let Some(parent) = parent_weak.upgrade() { - self.collect_required_methods(&parent, required_methods)?; - } - } - - Ok(()) -} -``` - -## 13. Conclusion - -The inheritance and interface system in WFL provides a powerful way to organize code and enable code reuse. By implementing single inheritance for containers and multiple interface implementation, we can support a wide range of object-oriented programming patterns while avoiding the complexities of multiple inheritance. - -The memory management strategies, particularly the use of weak references and caching, ensure that the system is efficient and avoids memory leaks. The type checking system ensures that containers correctly implement their interfaces, providing compile-time safety. \ No newline at end of file diff --git a/memory_optimization.md b/memory_optimization.md deleted file mode 100644 index 26cdf74c..00000000 --- a/memory_optimization.md +++ /dev/null @@ -1,747 +0,0 @@ -# Memory Optimization Strategies for WFL Container Implementation - -This document outlines the memory optimization strategies that will be employed in the WFL container implementation to minimize memory allocations and avoid reference cycles. - -## 1. Overview of Memory Challenges - -Container systems in programming languages often face several memory-related challenges: - -1. **Reference Cycles**: Container instances may reference their container definitions, which in turn may reference parent containers, creating potential reference cycles. -2. **Deep Inheritance Chains**: Resolving properties and methods in deep inheritance chains can be expensive. -3. **Event Handler Leaks**: Event handlers may hold references to container instances, preventing garbage collection. -4. **String Duplication**: Property and method names may be duplicated across many container instances. -5. **Temporary Object Allocations**: Method calls and property access may create many temporary objects. - -## 2. Reference Management Architecture - -```mermaid -graph TD - A[Container Definition] -->|strong reference| B[Methods] - A -->|strong reference| C[Properties] - A -->|weak reference| D[Parent Container] - - E[Container Instance] -->|weak reference| A - E -->|strong reference| F[Property Values] - - G[Event Handler] -->|weak reference| E - H[Method Environment] -->|weak reference| I[Parent Environment] -``` - -## 3. Weak References for Cycle Prevention - -### 3.1 Container Inheritance Cycles - -Container definitions will use weak references to their parent containers to prevent reference cycles in the inheritance chain: - -```rust -pub struct ContainerValue { - pub name: String, - pub extends: Option>, // Weak reference to avoid cycles - // Other fields... -} -``` - -This ensures that child containers don't keep their parent containers alive, allowing proper garbage collection of unused containers. - -### 3.2 Container Instance to Definition References - -Container instances will use weak references to their container definitions: - -```rust -pub struct ContainerInstanceValue { - pub container: Weak, // Weak reference to avoid cycles - pub properties: HashMap, - // Other fields... -} -``` - -This allows container definitions to be garbage collected when they're no longer needed, even if instances still exist. - -### 3.3 Environment References - -Method environments will use weak references to their parent environments: - -```rust -pub struct Environment { - pub values: HashMap, - pub parent: Option>>, // Weak reference to avoid cycles -} -``` - -This prevents reference cycles between environments and allows proper garbage collection. - -### 3.4 Event Handler References - -Event handlers will use weak references to their source objects: - -```rust -pub struct EventHandler { - pub source: Weak>, // Weak reference to avoid cycles - pub event_name: String, - pub handler: Rc, -} -``` - -This prevents event handlers from keeping container instances alive when they're no longer needed. - -## 4. Caching Strategies - -### 4.1 Method Resolution Caching - -To avoid repeated lookups in inheritance chains, we'll cache resolved methods: - -```rust -pub struct ContainerValue { - // Other fields... - pub method_cache: RefCell, Rc)>>, -} -``` - -The cache stores the method and a weak reference to the container where it was found. This improves performance for method calls on containers with deep inheritance chains. - -### 4.2 Property Resolution Caching - -Similarly, we'll cache resolved properties: - -```rust -pub struct ContainerValue { - // Other fields... - pub property_cache: RefCell, PropertyDefinition)>>, -} -``` - -This improves performance for property access on containers with deep inheritance chains. - -### 4.3 Cache Invalidation - -When a container is modified, we need to invalidate its caches: - -```rust -fn invalidate_caches(&self, container: &ContainerValue) { - container.method_cache.borrow_mut().clear(); - container.property_cache.borrow_mut().clear(); - - // Also invalidate caches of child containers - // This would require maintaining a list of weak references to child containers -} -``` - -### 4.4 Lazy Loading - -Instead of eagerly loading all properties and methods from parent containers, we'll use lazy loading: - -```rust -fn get_property(&self, name: &str) -> Option { - // Check own properties first - if let Some(value) = self.properties.get(name) { - return Some(value.clone()); - } - - // Check cached properties - if let Some((_, value)) = self.property_cache.borrow().get(name) { - return Some(value.clone()); - } - - // Check parent container - if let Some(parent_weak) = &self.container.extends { - if let Some(parent) = parent_weak.upgrade() { - if let Some(value) = parent.get_property(name) { - // Cache the result - self.property_cache.borrow_mut().insert( - name.to_string(), - (Rc::downgrade(&parent), value.clone()) - ); - return Some(value); - } - } - } - - None -} -``` - -This ensures we only load properties and methods when they're actually needed. - -## 5. String Interning - -### 5.1 String Interning for Property and Method Names - -To avoid duplicating strings for property and method names, we'll use a global string interner: - -```rust -pub struct StringInterner { - strings: HashMap>, -} - -impl StringInterner { - pub fn new() -> Self { - Self { - strings: HashMap::new(), - } - } - - pub fn intern(&mut self, s: &str) -> Rc { - if let Some(interned) = self.strings.get(s) { - interned.clone() - } else { - let rc = Rc::from(s.to_string()); - self.strings.insert(s.to_string(), rc.clone()); - rc - } - } -} -``` - -This ensures that identical strings are only stored once in memory. - -### 5.2 Integration with Parser - -The parser will use the string interner for all identifiers: - -```rust -fn parse_identifier(&mut self) -> Result, ParseError> { - if let Some(token) = self.tokens.peek() { - if let Token::Identifier(id) = &token.token { - self.tokens.next(); - Ok(self.string_interner.intern(id)) - } else { - Err(ParseError::new( - format!("Expected identifier, found {:?}", token.token), - token.line, - token.column, - )) - } - } else { - Err(ParseError::new( - "Expected identifier, found end of input".to_string(), - 0, - 0, - )) - } -} -``` - -### 5.3 Integration with Value System - -The value system will use interned strings for property and method names: - -```rust -pub struct ContainerValue { - pub name: Rc, - pub properties: HashMap, PropertyDefinition>, - pub methods: HashMap, Rc>, - // Other fields... -} - -pub struct ContainerInstanceValue { - pub container: Weak, - pub properties: HashMap, Value>, - // Other fields... -} -``` - -This reduces memory usage and improves lookup performance by allowing direct pointer comparison for strings. - -## 6. Object Pooling - -### 6.1 Event Handler Context Pooling - -For frequently triggered events, we'll use an object pool for handler execution contexts: - -```rust -pub struct EventHandlerContext { - pub arguments: HashMap, - pub result: Option, -} - -pub struct EventHandlerPool { - pub available: Vec, - pub capacity: usize, -} - -impl EventHandlerPool { - pub fn new(capacity: usize) -> Self { - let mut available = Vec::with_capacity(capacity); - for _ in 0..capacity { - available.push(EventHandlerContext { - arguments: HashMap::new(), - result: None, - }); - } - - Self { - available, - capacity, - } - } - - pub fn acquire(&mut self) -> EventHandlerContext { - if let Some(context) = self.available.pop() { - context - } else { - EventHandlerContext { - arguments: HashMap::new(), - result: None, - } - } - } - - pub fn release(&mut self, mut context: EventHandlerContext) { - context.arguments.clear(); - context.result = None; - - if self.available.len() < self.capacity { - self.available.push(context); - } - } -} -``` - -This reduces the number of allocations when handling events. - -### 6.2 Property Access Context Pooling - -Similarly, we'll use an object pool for property access contexts: - -```rust -pub struct PropertyAccessContext { - pub container: Weak, - pub property_name: Rc, - pub result: Option, -} - -pub struct PropertyAccessPool { - pub available: Vec, - pub capacity: usize, -} -``` - -This reduces allocations during property access operations. - -## 7. Efficient Data Structures - -### 7.1 HashMap Optimization - -We'll use capacity hints for HashMaps to avoid reallocations: - -```rust -pub fn new_container_instance(container: &Rc) -> Rc> { - let property_count = container.properties.len(); - - Rc::new(RefCell::new(ContainerInstanceValue { - container: Rc::downgrade(container), - properties: HashMap::with_capacity(property_count), - event_handlers: HashMap::new(), - })) -} -``` - -### 7.2 Vector Optimization - -Similarly, we'll use capacity hints for Vectors: - -```rust -pub fn collect_event_handlers(&self, event_name: &str) -> Vec> { - let mut handlers = Vec::with_capacity(4); // Most events have few handlers - - if let Some(event_handlers) = self.event_handlers.get(event_name) { - handlers.extend(event_handlers.iter().cloned()); - } - - handlers -} -``` - -### 7.3 Small Vector Optimization - -For collections that are typically small, we'll use small vector optimization: - -```rust -pub enum SmallVec { - Inline([Option; 4]), - Heap(Vec), -} - -impl SmallVec { - pub fn new() -> Self { - Self::Inline([None, None, None, None]) - } - - pub fn push(&mut self, value: T) { - match self { - Self::Inline(array) => { - for slot in array.iter_mut() { - if slot.is_none() { - *slot = Some(value); - return; - } - } - - // Array is full, convert to heap - let mut vec = Vec::with_capacity(8); - for item in array.iter_mut() { - if let Some(v) = item.take() { - vec.push(v); - } - } - vec.push(value); - *self = Self::Heap(vec); - } - Self::Heap(vec) => { - vec.push(value); - } - } - } - - // Other methods... -} -``` - -This avoids heap allocations for small collections. - -## 8. Memory-Efficient Value Representation - -### 8.1 Value Enum Optimization - -We'll optimize the `Value` enum to reduce its size: - -```rust -pub enum Value { - Number(f64), - Text(Rc), - Bool(bool), - List(Rc>>), - Object(Rc, Value>>>), - Function(Rc), - NativeFunction(NativeFunction), - Container(Rc), - ContainerInstance(Rc>), - Interface(Rc), - Event(Rc), - Future(Rc>), - Null, -} -``` - -### 8.2 Small String Optimization - -For small strings, we'll use small string optimization: - -```rust -pub enum SmallString { - Inline([u8; 24], usize), // Buffer and length - Heap(Rc), -} - -impl SmallString { - pub fn new(s: &str) -> Self { - if s.len() <= 24 { - let mut buffer = [0u8; 24]; - buffer[..s.len()].copy_from_slice(s.as_bytes()); - Self::Inline(buffer, s.len()) - } else { - Self::Heap(Rc::from(s)) - } - } - - pub fn as_str(&self) -> &str { - match self { - Self::Inline(buffer, len) => { - std::str::from_utf8(&buffer[..*len]).unwrap() - } - Self::Heap(rc) => rc, - } - } -} -``` - -This avoids heap allocations for small strings. - -## 9. Lazy Evaluation - -### 9.1 Lazy Property Initialization - -Properties with default values will be initialized lazily: - -```rust -fn get_property_value( - &self, - instance: &ContainerInstanceValue, - property_name: &str, -) -> Result { - // Check if the property exists in the instance - if let Some(value) = instance.properties.get(property_name) { - return Ok(value.clone()); - } - - // Look up the property definition - let property = match self.resolve_property(&instance.container, property_name) { - Some(p) => p, - None => return Err(RuntimeError::new( - format!("Property '{}' not found", property_name), - /* line */, - /* column */, - )), - }; - - // If the property has a default value, initialize it - if let Some(default_value) = &property.default_value { - let value = self.evaluate_expression(default_value, /* environment */)?; - instance.properties.insert(property_name.to_string(), value.clone()); - Ok(value) - } else { - Err(RuntimeError::new( - format!("Property '{}' not initialized", property_name), - /* line */, - /* column */, - )) - } -} -``` - -This ensures that default values are only computed when needed. - -### 9.2 Lazy Interface Validation - -Interface validation will be performed lazily: - -```rust -fn validate_interface_implementation( - &self, - container: &ContainerValue, - interface: &InterfaceValue, -) -> Result<(), RuntimeError> { - // Check if validation has already been performed - if container.validated_interfaces.borrow().contains(&interface.name) { - return Ok(()); - } - - // Perform validation - for (method_name, signature) in &interface.required_actions { - // Check if the container has the method - let method = match self.resolve_method(container, method_name) { - Some(m) => m, - None => return Err(RuntimeError::new( - format!( - "Container '{}' does not implement required method '{}' from interface '{}'", - container.name, method_name, interface.name - ), - /* line */, - /* column */, - )), - }; - - // Check if the method signature matches - // ... - } - - // Mark interface as validated - container.validated_interfaces.borrow_mut().insert(interface.name.clone()); - - Ok(()) -} -``` - -This ensures that interface validation is only performed once per container-interface pair. - -## 10. Memory Cleanup - -### 10.1 Explicit Cleanup - -We'll implement explicit cleanup methods for container instances: - -```rust -impl ContainerInstanceValue { - pub fn cleanup(&mut self) { - // Clear properties - self.properties.clear(); - - // Clear event handlers - self.event_handlers.clear(); - - // Clear method cache - if let Some(container) = self.container.upgrade() { - container.method_cache.borrow_mut().clear(); - container.property_cache.borrow_mut().clear(); - } - } -} -``` - -### 10.2 Drop Implementation - -We'll implement the `Drop` trait for container instances to ensure proper cleanup: - -```rust -impl Drop for ContainerInstanceValue { - fn drop(&mut self) { - // Clear event handlers to break potential reference cycles - self.event_handlers.clear(); - - // Clear properties to break potential reference cycles - self.properties.clear(); - } -} -``` - -### 10.3 Weak Reference Handling - -We'll handle weak references carefully to avoid dereferencing dangling pointers: - -```rust -fn resolve_method( - &self, - container_weak: &Weak, - method_name: &str, -) -> Option> { - // Upgrade weak reference - let container = match container_weak.upgrade() { - Some(c) => c, - None => return None, // Container no longer exists - }; - - // Check own methods - if let Some(method) = container.methods.get(method_name) { - return Some(method.clone()); - } - - // Check parent container - if let Some(parent_weak) = &container.extends { - return self.resolve_method(parent_weak, method_name); - } - - None -} -``` - -## 11. Memory Profiling and Optimization - -### 11.1 Memory Profiling Tools - -We'll use memory profiling tools to identify memory usage patterns: - -- **DHAT**: Heap profiling via `dhat-heap` feature -- **Valgrind**: Memory leak detection -- **Custom Memory Tracker**: Track allocations and deallocations - -### 11.2 Memory Benchmarks - -We'll create benchmarks to measure memory usage: - -```rust -#[bench] -fn bench_container_creation(b: &mut Bencher) { - b.iter(|| { - let mut interpreter = Interpreter::new(); - let program = parse_program(r#" - create container Test: - property name as text - property value as number - - define action initialize with n and v: - set name to n - set value to v - end action - end container - - create new Test with "test" and 42 as instance - "#); - - interpreter.interpret(&program).unwrap(); - }); -} -``` - -### 11.3 Memory Optimization Workflow - -1. **Profile**: Use memory profiling tools to identify memory usage patterns -2. **Analyze**: Identify areas with high memory usage or leaks -3. **Optimize**: Apply memory optimization techniques -4. **Verify**: Re-profile to ensure optimizations are effective -5. **Repeat**: Continue until memory usage is acceptable - -## 12. Implementation Guidelines - -### 12.1 General Guidelines - -1. **Prefer Stack Allocation**: Use stack allocation when possible -2. **Minimize Cloning**: Avoid unnecessary cloning of values -3. **Use References**: Pass references instead of values when possible -4. **Reuse Objects**: Reuse objects instead of creating new ones -5. **Avoid Temporary Objects**: Minimize creation of temporary objects - -### 12.2 Container-Specific Guidelines - -1. **Lazy Property Initialization**: Initialize properties lazily -2. **Method Resolution Caching**: Cache resolved methods -3. **Property Resolution Caching**: Cache resolved properties -4. **String Interning**: Use string interning for property and method names -5. **Weak References**: Use weak references to avoid reference cycles - -### 12.3 Code Examples - -#### Minimizing Cloning - -```rust -// Bad: Clones value unnecessarily -fn get_property(&self, name: &str) -> Value { - self.properties.get(name).unwrap().clone() -} - -// Good: Returns reference to avoid cloning -fn get_property(&self, name: &str) -> &Value { - self.properties.get(name).unwrap() -} -``` - -#### Reusing Objects - -```rust -// Bad: Creates new HashMap for each call -fn get_property_values(&self) -> HashMap { - let mut result = HashMap::new(); - for (name, value) in &self.properties { - result.insert(name.clone(), value.clone()); - } - result -} - -// Good: Reuses provided HashMap -fn get_property_values(&self, result: &mut HashMap) { - for (name, value) in &self.properties { - result.insert(name.clone(), value.clone()); - } -} -``` - -#### Using Weak References - -```rust -// Bad: Creates reference cycle -struct Container { - parent: Option>, - children: Vec>, -} - -// Good: Avoids reference cycle -struct Container { - parent: Option>, - children: Vec>, -} -``` - -## 13. Conclusion - -By implementing these memory optimization strategies, we can ensure that the WFL container system is memory-efficient and avoids common memory-related issues such as reference cycles and excessive allocations. These strategies will be particularly important for applications that create many container instances or have deep inheritance hierarchies. - -The key strategies are: - -1. **Weak References**: Use weak references to avoid reference cycles -2. **Caching**: Cache resolved methods and properties to improve performance -3. **String Interning**: Use string interning to reduce memory usage -4. **Object Pooling**: Use object pools to reduce allocations -5. **Lazy Evaluation**: Initialize properties and validate interfaces lazily -6. **Efficient Data Structures**: Use capacity hints and small vector optimization -7. **Memory Cleanup**: Implement explicit cleanup and proper drop behavior - -By following these strategies, we can create a container system that is both powerful and memory-efficient. \ No newline at end of file diff --git a/memory_optimization_results.md b/memory_optimization_results.md deleted file mode 100644 index c0091382..00000000 --- a/memory_optimization_results.md +++ /dev/null @@ -1,25 +0,0 @@ -# Memory Optimization Results - -## Baseline Metrics -- Allocations: 232,277,040 -- Peak heap memory: 25.60GB -- Temporary allocations: High (millions) - -## Optimized Metrics -- Allocations: 279 -- Peak heap memory: 189.88K -- Peak RSS: 8.71M -- Temporary allocations: 12 -- Total memory leaked: 29.22K - -## Improvement Summary -- **Allocation reduction**: 99.999% (232M → 279) -- **Memory usage reduction**: 99.999% (25.60GB → 189.88K) - -## Optimization Techniques Applied -1. **Token borrowing**: Replaced `peek().cloned()` with references to avoid unnecessary cloning -2. **Vector preallocation**: Used `Vec::with_capacity()` to reduce reallocations -3. **String interning**: Implemented string pooling for identifiers and string literals -4. **Error message optimization**: Deferred formatting until needed - -These optimizations have successfully reduced the parser's memory consumption well beyond the 80% target, achieving a >99.9% reduction in both allocations and peak memory usage. diff --git a/nested_non_existent.txt b/nested_non_existent.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/non_existent_file.txt b/non_existent_file.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/output.txt b/output.txt deleted file mode 100644 index 2b270839..00000000 Binary files a/output.txt and /dev/null differ diff --git a/param_binding_test.wfl b/param_binding_test.wfl deleted file mode 100644 index 27386ae8..00000000 --- a/param_binding_test.wfl +++ /dev/null @@ -1,9 +0,0 @@ -// Test for parameter binding with space-separated parameter names -define action called test_binding needs param1 param2 param3: - display "param1: " with param1 - display "param2: " with param2 - display "param3: " with param3 -end action - -// Call the action with a single argument -test_binding with "This is a single argument" \ No newline at end of file diff --git a/param_binding_test2.wfl b/param_binding_test2.wfl deleted file mode 100644 index 683fe778..00000000 --- a/param_binding_test2.wfl +++ /dev/null @@ -1,9 +0,0 @@ -// Test for parameter binding with 'and' between parameter names -define action called test_binding_with_and needs param1 and param2 and param3: - display "param1: " with param1 - display "param2: " with param2 - display "param3: " with param3 -end action - -// Call the action with a single argument -test_binding_with_and with "This is a single argument" \ No newline at end of file diff --git a/param_binding_test3.wfl b/param_binding_test3.wfl deleted file mode 100644 index dda3ee30..00000000 --- a/param_binding_test3.wfl +++ /dev/null @@ -1,19 +0,0 @@ -// Test for parameter binding with mixed syntax -define action called test_mixed needs label expected actual: - display "label: " with label - display "expected: " with expected - display "actual: " with actual -end action - -// Call with a single argument -test_mixed with "This is a single argument" - -// Define another action with 'and' syntax -define action called test_with_and needs first and second and third: - display "first: " with first - display "second: " with second - display "third: " with third -end action - -// Call with three arguments -test_with_and with "Arg1" and "Arg2" and "Arg3" \ No newline at end of file diff --git a/pattern_debug.txt b/pattern_debug.txt deleted file mode 100644 index 38141fd9..00000000 --- a/pattern_debug.txt +++ /dev/null @@ -1,37 +0,0 @@ -Parse errors: -error[ERROR]: Unexpected token in pattern: KeywordBy - ┌─ TestPrograms/pattern_backreference_test.wfl:11:33 - │ -11 │ capture {any letter} as word followed by same as captured "word" - │ ^ Error occurred here - -error[ERROR]: Unexpected token in pattern: KeywordBy - ┌─ TestPrograms/pattern_backreference_test.wfl:34:18 - │ -34 │ capture {one or more letter} as word followed by " " followed by same as captured "word" - │ ^ Error occurred here - -error[ERROR]: Unexpected token in pattern: KeywordBy - ┌─ TestPrograms/pattern_backreference_test.wfl:57:10 - │ -57 │ "<" followed by capture {one or more letter} as tag followed by ">" followed by zero or more any letter followed by "" - │ ^ Error occurred here - -error[ERROR]: Unexpected token in pattern: KeywordBy - ┌─ TestPrograms/pattern_backreference_test.wfl:79:1 - │ -79 │ create pattern find_repeat: - │ ^ Error occurred here - -error[ERROR]: Unexpected token in pattern: KeywordBy - ┌─ TestPrograms/pattern_backreference_test.wfl:94:10 - │ -94 │ display "Test 5: Multiple captures" - │ ^ Error occurred here - -error[ERROR]: Unexpected token in pattern: KeywordBy - ┌─ TestPrograms/pattern_backreference_test.wfl:117:9 - │ -117 │ display "Test 6: Backreference with quantifiers" - │ ^ Error occurred here - diff --git a/plan.md b/plan.md deleted file mode 100644 index 9b40bbd8..00000000 --- a/plan.md +++ /dev/null @@ -1,718 +0,0 @@ -# Container Implementation Plan for WFL - -## 1. Overview - -Based on the documentation in `wfl-containers.md` and `wfl-actions.md`, we need to implement a complete container system that allows users to define custom data types with properties and behaviors. The implementation must: - -- Support all features described in the documentation -- Minimize memory allocations -- Avoid reference cycles -- Maintain backward compatibility -- Follow WFL's natural language approach - -## 2. Architecture Design - -```mermaid -graph TD - A[AST Extensions] --> B[Parser Updates] - B --> C[Value System Extensions] - C --> D[Environment System Updates] - D --> E[Interpreter Implementation] - E --> F[Type Checker Integration] - - subgraph "Memory Management" - G[Weak References] - H[Reference Counting] - I[Memory Pooling] - end - - G --> C - H --> C - I --> C -``` - -## 3. Detailed Implementation Plan - -### 3.1 AST Extensions - -We need to extend the AST structure in `src/parser/ast.rs` to support container-related constructs: - -```rust -// Add to Statement enum -ContainerDefinition { - name: String, - extends: Option, - implements: Vec, - properties: Vec, - methods: Vec, // ActionDefinition statements - events: Vec, - static_properties: Vec, - static_methods: Vec, - line: usize, - column: usize, -}, - -ContainerInstantiation { - container_type: String, - instance_name: String, - arguments: Vec, - property_initializers: Vec, - line: usize, - column: usize, -}, - -InterfaceDefinition { - name: String, - required_actions: Vec, - line: usize, - column: usize, -}, - -EventDefinition { - name: String, - parameters: Vec, - line: usize, - column: usize, -}, - -EventTrigger { - name: String, - arguments: Vec, - line: usize, - column: usize, -}, - -EventHandler { - event_source: Expression, - event_name: String, - handler_body: Vec, - line: usize, - column: usize, -}, -``` - -Supporting structures: - -```rust -pub struct PropertyDefinition { - pub name: String, - pub property_type: Option, - pub default_value: Option, - pub validation_rules: Vec, - pub visibility: Visibility, - pub is_static: bool, - pub line: usize, - pub column: usize, -} - -pub struct ValidationRule { - pub rule_type: ValidationRuleType, - pub parameters: Vec, - pub line: usize, - pub column: usize, -} - -pub enum ValidationRuleType { - NotEmpty, - MinLength, - MaxLength, - ExactLength, - MinValue, - MaxValue, - Pattern, - Custom, -} - -pub enum Visibility { - Public, - Private, -} - -pub struct PropertyInitializer { - pub name: String, - pub value: Expression, - pub line: usize, - pub column: usize, -} - -pub struct ActionSignature { - pub name: String, - pub parameters: Vec, - pub return_type: Option, -} -``` - -### 3.2 Value System Extensions - -Extend the value system in `src/interpreter/value.rs` to support container-related values: - -```rust -// Add to Value enum -Container(Rc), -ContainerInstance(Rc>), -Interface(Rc), -Event(Rc), -``` - -Supporting structures with memory optimization: - -```rust -pub struct ContainerValue { - pub name: String, - pub extends: Option>, // Weak reference to avoid cycles - pub implements: Vec>, // Weak references to avoid cycles - pub properties: HashMap, - pub methods: HashMap>, - pub static_properties: HashMap, - pub static_methods: HashMap>, - pub events: HashMap>, - // Cache for inherited properties and methods to avoid repeated lookups - pub property_cache: RefCell, PropertyDefinition)>>, - pub method_cache: RefCell, Rc)>>, -} - -pub struct ContainerInstanceValue { - pub container: Weak, // Weak reference to avoid cycles - pub properties: HashMap, - pub env: Weak>, // Weak reference to avoid cycles - pub event_handlers: HashMap>>, -} - -pub struct InterfaceValue { - pub name: String, - pub required_actions: HashMap, -} - -pub struct EventValue { - pub name: String, - pub parameters: Vec, -} -``` - -## 4. Event System Implementation - -The event system is a critical part of the container implementation, enabling reactive programming patterns. This section provides a detailed explanation of how events, triggers, and handlers will be implemented. - -### 4.1 Event System Architecture - -```mermaid -graph TD - A[Event Definition] --> B[Event Registration] - B --> C[Event Triggering] - C --> D[Handler Execution] - - E[Container Definition] --> A - F[Container Instance] --> B - F --> C - G[Event Handler] --> D -``` - -### 4.2 Event Definition - -Events are defined within container definitions using the `event` keyword: - -```wfl -create container Button: - property label as text - - // Event definitions - event clicked - event hover start with x and y - event hover end -end container -``` - -In the AST, events are represented as `EventDefinition` structures: - -```rust -pub struct EventDefinition { - pub name: String, - pub parameters: Vec, - pub line: usize, - pub column: usize, -} -``` - -During parsing, the `parse_event_definition` function will extract the event name and any parameters: - -```rust -fn parse_event_definition(&mut self) -> Result { - self.expect_token(Token::KeywordEvent, "Expected 'event'")?; - - let name = self.parse_identifier()?; - - let mut parameters = Vec::new(); - - // Check if there are parameters (with keyword) - if self.match_token(Token::KeywordWith) { - parameters = self.parse_parameter_list()?; - } - - Ok(EventDefinition { - name, - parameters, - line: /* current line */, - column: /* current column */, - }) -} -``` - -### 4.3 Event Storage - -Events are stored in the `ContainerValue` structure: - -```rust -pub struct ContainerValue { - // Other fields... - pub events: HashMap>, -} - -pub struct EventValue { - pub name: String, - pub parameters: Vec, -} -``` - -When a container is defined, its events are registered in this map: - -```rust -async fn execute_container_definition(&self, /* params */) -> Result { - // Create container value - let container = Rc::new(ContainerValue { - // Other fields... - events: HashMap::new(), - }); - - // Register events - for event_def in events { - let event_value = Rc::new(EventValue { - name: event_def.name.clone(), - parameters: event_def.parameters.iter().map(|p| p.name.clone()).collect(), - }); - - container.events.insert(event_def.name.clone(), event_value); - } - - // Register container in environment - env.borrow_mut().define(&name, Value::Container(container)); - - Ok(Value::Null) -} -``` - -### 4.4 Event Handlers - -Event handlers are defined using the `on` keyword followed by an event source, event name, and handler body: - -```wfl -create new Button as submit_button: - set label to "Submit" -end create - -// Event handler registration -on submit_button clicked: - display "Button was clicked!" -end on -``` - -In the AST, event handlers are represented as `EventHandler` structures: - -```rust -pub struct EventHandler { - pub event_source: Expression, - pub event_name: String, - pub handler_body: Vec, - pub line: usize, - pub column: usize, -} -``` - -The parser will extract the event source, event name, and handler body: - -```rust -fn parse_event_handler(&mut self) -> Result { - self.expect_token(Token::KeywordOn, "Expected 'on'")?; - - let event_source = self.parse_expression()?; - let event_name = self.parse_identifier()?; - - self.expect_token(Token::Colon, "Expected ':' after event name")?; - - let handler_body = self.parse_block()?; - - self.expect_token(Token::KeywordEnd, "Expected 'end'")?; - self.expect_token(Token::KeywordOn, "Expected 'on' after 'end'")?; - - Ok(Statement::EventHandler { - event_source, - event_name, - handler_body, - line: /* current line */, - column: /* current column */, - }) -} -``` - -### 4.5 Event Handler Registration - -Event handlers are stored in the `ContainerInstanceValue` structure: - -```rust -pub struct ContainerInstanceValue { - // Other fields... - pub event_handlers: HashMap>>, -} -``` - -When an event handler is defined, it's registered with the container instance: - -```rust -async fn execute_event_handler( - &self, - event_source: &Expression, - event_name: &str, - handler_body: &[Statement], - env: Rc>, -) -> Result { - // Evaluate event source to get container instance - let source_value = self.evaluate_expression(event_source, Rc::clone(&env)).await?; - - match source_value { - Value::ContainerInstance(instance) => { - let mut instance_ref = instance.borrow_mut(); - - // Check if the event exists on the container - let container = match instance_ref.container.upgrade() { - Some(c) => c, - None => return Err(RuntimeError::new( - "Container no longer exists".to_string(), - /* line */, - /* column */, - )), - }; - - if !container.events.contains_key(event_name) { - return Err(RuntimeError::new( - format!("Event '{}' not found on container", event_name), - /* line */, - /* column */, - )); - } - - // Create function value for handler - let handler = Rc::new(FunctionValue { - name: Some(format!("{}_{}_handler", instance_ref.container.name, event_name)), - params: vec![], // Event parameters will be passed when triggered - body: handler_body.to_vec(), - env: Rc::downgrade(&env), - line: /* line */, - column: /* column */, - }); - - // Register handler - instance_ref.event_handlers - .entry(event_name.to_string()) - .or_insert_with(Vec::new) - .push(handler); - - Ok(Value::Null) - }, - _ => Err(RuntimeError::new( - "Event source must be a container instance".to_string(), - /* line */, - /* column */, - )), - } -} -``` - -### 4.6 Event Triggering - -Events are triggered using the `trigger` keyword: - -```wfl -define action click: - if is_enabled: - trigger clicked // Trigger event with no parameters - display "Button was clicked" - end if -end action - -define action hover at with x_pos and y_pos: - trigger hover start with x_pos and y_pos // Trigger event with parameters - display "Hovering at " with x_pos with "," with y_pos -end action -``` - -In the AST, event triggers are represented as `EventTrigger` structures: - -```rust -pub struct EventTrigger { - pub name: String, - pub arguments: Vec, - pub line: usize, - pub column: usize, -} -``` - -The parser will extract the event name and any arguments: - -```rust -fn parse_event_trigger(&mut self) -> Result { - self.expect_token(Token::KeywordTrigger, "Expected 'trigger'")?; - - let name = self.parse_identifier()?; - - let mut arguments = Vec::new(); - - // Check if there are arguments (with keyword) - if self.match_token(Token::KeywordWith) { - arguments = self.parse_argument_list()?; - } - - Ok(Statement::EventTrigger { - name, - arguments, - line: /* current line */, - column: /* current column */, - }) -} -``` - -### 4.7 Event Handler Execution - -When an event is triggered, all registered handlers for that event are executed: - -```rust -async fn execute_event_trigger( - &self, - name: &str, - arguments: &[Argument], - env: Rc>, -) -> Result { - // Get the current container instance from the environment - let this_value = match env.borrow().get("this") { - Some(Value::ContainerInstance(instance)) => Value::ContainerInstance(instance), - _ => return Err(RuntimeError::new( - "Cannot trigger event outside of container method".to_string(), - /* line */, - /* column */, - )), - }; - - match this_value { - Value::ContainerInstance(instance) => { - let instance_ref = instance.borrow(); - - // Check if the event exists on the container - let container = match instance_ref.container.upgrade() { - Some(c) => c, - None => return Err(RuntimeError::new( - "Container no longer exists".to_string(), - /* line */, - /* column */, - )), - }; - - if !container.events.contains_key(name) { - return Err(RuntimeError::new( - format!("Event '{}' not found on container", name), - /* line */, - /* column */, - )); - } - - // Evaluate arguments - let mut arg_values = Vec::new(); - for arg in arguments { - let value = self.evaluate_expression(&arg.value, Rc::clone(&env)).await?; - arg_values.push(value); - } - - // Get handlers for this event - if let Some(handlers) = instance_ref.event_handlers.get(name) { - // Execute each handler - for handler in handlers { - // Create a new environment for the handler - let handler_env = Environment::new(&env); - - // Add arguments to environment - let event_params = &container.events.get(name).unwrap().parameters; - for (i, param) in event_params.iter().enumerate() { - if i < arg_values.len() { - handler_env.borrow_mut().define(param, arg_values[i].clone()); - } else { - handler_env.borrow_mut().define(param, Value::Null); - } - } - - // Execute handler - self.execute_block(&handler.body, Rc::clone(&handler_env)).await?; - } - } - - Ok(Value::Null) - }, - _ => Err(RuntimeError::new( - "Cannot trigger event outside of container method".to_string(), - /* line */, - /* column */, - )), - } -} -``` - -### 4.8 Event Inheritance - -Events are inherited from parent containers: - -```wfl -create container UIElement: - event clicked - event focus - event blur -end container - -create container Button extends UIElement: - event hover // Adds a new event - // Inherits clicked, focus, and blur events -end container -``` - -When resolving events, the interpreter checks the container and all its ancestors: - -```rust -fn resolve_event(&self, container: &ContainerValue, name: &str) -> Option> { - // Check own events - if let Some(event) = container.events.get(name) { - return Some(event.clone()); - } - - // Check parent container - if let Some(parent_weak) = &container.extends { - if let Some(parent) = parent_weak.upgrade() { - return self.resolve_event(&parent, name); - } - } - - None -} -``` - -### 4.9 Memory Management for Events - -To avoid memory leaks and reference cycles, we'll use several strategies: - -1. **Weak References for Container References**: - ```rust - pub struct ContainerInstanceValue { - pub container: Weak, // Weak reference to avoid cycles - // Other fields... - } - ``` - -2. **Handler Cleanup**: - When a container instance is dropped, its event handlers should be cleaned up: - ```rust - impl Drop for ContainerInstanceValue { - fn drop(&mut self) { - // Clear event handlers to break potential reference cycles - self.event_handlers.clear(); - } - } - ``` - -3. **Event Handler Pool**: - For frequently triggered events, we can use an object pool for handler execution contexts: - ```rust - pub struct EventHandlerContext { - pub arguments: HashMap, - pub result: Option, - } - - pub struct EventHandlerPool { - pub available: Vec, - pub capacity: usize, - } - ``` - -### 4.10 Event System Error Handling - -Specific error types for the event system: - -```rust -// Add to ErrorKind enum -EventNotFound, -EventHandlerError, -InvalidEventArguments, -``` - -Error messages: -- "Event '{0}' not found on container '{1}'" -- "Error in event handler: {0}" -- "Invalid arguments for event '{0}': expected {1}, got {2}" - -## 5. Implementation Phases - -### 5.1 Phase 1: Basic Container Support -- AST extensions for container definitions and instantiation -- Parser updates for basic container syntax -- Value system extensions for containers -- Basic interpreter support for containers - -### 5.2 Phase 2: Properties and Methods -- Property definitions with validation -- Method definitions and calls -- Container instantiation -- Memory optimization foundations - -### 5.3 Phase 3: Inheritance and Interfaces -- Container inheritance -- Interface definitions and implementation -- Composition (containers as properties) -- Static members - -### 5.4 Phase 4: Event System -- Event definitions -- Event triggers -- Event handlers -- Event inheritance -- Memory optimizations for events - -### 5.5 Phase 5: Integration and Testing -- Type checker integration -- Error handling improvements -- Documentation updates -- Comprehensive testing - -## 6. Testing Strategy - -### 6.1 Unit Tests - -1. **Event System Tests**: - - Test event definition parsing - - Test event handler registration - - Test event triggering - - Test event inheritance - - Test event parameters - -2. **Memory Tests**: - - Test for memory leaks in event handlers - - Test for reference cycles in event system - - Test memory usage with many event handlers - -### 6.2 Integration Tests - -1. **Feature Tests**: - - Test events working with inheritance - - Test complex event chains - - Test event handlers accessing container state - -2. **Error Handling Tests**: - - Test triggering undefined events - - Test invalid event arguments - - Test event handler errors - -## 7. Conclusion - -This implementation plan provides a comprehensive approach to adding container functionality to WFL, with special focus on the event system. By following this plan, we can ensure that the implementation is robust, memory-efficient, and aligns with WFL's natural language approach to programming. \ No newline at end of file diff --git a/pr_description.md b/pr_description.md deleted file mode 100644 index 7d3f7577..00000000 --- a/pr_description.md +++ /dev/null @@ -1,68 +0,0 @@ -# Debug Output Refactoring PR - -## Summary - -This PR refactors the debug output system in the WFL interpreter and parser to ensure all debug messages go through the logging system rather than directly to stdout. It standardizes the use of `exec_trace!` macros throughout the codebase and adjusts memory usage test thresholds to account for the additional logging overhead. - -## Changes - -- Replaced all direct `println!("DEBUG: ...")` statements with `exec_trace!` macros -- Added missing `use crate::exec_trace;` import to the parser module -- Fixed 12+ instances of debug output in the parser module -- Fixed 7+ instances of debug output in the interpreter module -- Updated documentation to reflect the changes -- Adjusted memory usage test thresholds to accommodate logging overhead -- Fixed failing memory usage tests while maintaining reasonable memory limits - -## Memory Usage Adjustments - -- Increased the memory threshold in `test_environment_memory_usage` from 20KB to 25KB -- Increased the memory threshold in `test_functions_memory_usage` from 15KB to 20KB -- These adjustments account for the additional memory overhead from enhanced debug logging -- The tests still correctly verify that environment reference counts are properly managed (no leaks) - -## Testing - -- Ran test.wfl script to verify the fixes work as expected -- Confirmed proper execution without debug messages in console output -- Verified log messages are correctly appended to nexus.log file -- Checked the AST dump to confirm concatenation expressions are properly parsed -- Verified memory usage tests now pass with the adjusted thresholds - -## Related Issues - -This PR completes the work started in the previous PR where we began standardizing the logging approach across the codebase. It also enhances our fix for the concatenation vs. action call parsing issue and addresses memory usage test failures caused by the enhanced logging. - -## Known Limitations - -- The static analyzer doesn't recognize variable usage inside `WaitForStatement` structures, resulting in false positive "unused variable" warnings -- Future enhancement could include updating the static analyzer to inspect the inner statement of `WaitForStatement` nodes for variable usage - -## Documentation - -- Added implementation_progress_2025-05-21.md with details of the changes -- Updated implementation progress to include memory threshold adjustment rationale -- No README updates needed as these are implementation details - -## Screenshots - -Before: Debug output mixed with program output -``` -DEBUG: call_function - Created child environment for function call -DEBUG: call_function - Binding parameter 0 'message_text' to argument Text("Starting Nexus WFL Integration Test Suite...") -DEBUG: call_function - Pushed frame to call stack -DEBUG: call_function - Executing function body -yes -yes -yes -yes -Fractional division test: PASS -``` - -After: Clean program output only -``` -yes -yes -yes -yes -Fractional division test: PASS diff --git a/src/analyzer/mod.rs b/src/analyzer/mod.rs index 7337556f..688095a3 100644 --- a/src/analyzer/mod.rs +++ b/src/analyzer/mod.rs @@ -83,16 +83,35 @@ impl Scope { } pub fn define(&mut self, symbol: Symbol) -> Result<(), SemanticError> { - if symbol.name == "currentLog" || !self.symbols.contains_key(&symbol.name) { - self.symbols.insert(symbol.name.clone(), symbol); - Ok(()) - } else { - Err(SemanticError::new( - format!("Symbol '{}' is already defined in this scope", symbol.name), + // Check if variable already exists in current scope + if self.symbols.contains_key(&symbol.name) { + let existing = &self.symbols[&symbol.name]; + return Err(SemanticError::new( + format!( + "Variable '{}' has already been defined at line {}. Use 'change {} to ' to modify it.", + symbol.name, existing.line, symbol.name + ), + symbol.line, + symbol.column, + )); + } + + // Check if variable exists in parent scopes + if let Some(parent) = &self.parent + && parent.resolve(&symbol.name).is_some() + { + return Err(SemanticError::new( + format!( + "Variable '{}' has already been defined in an outer scope. Use 'change {} to ' to modify it.", + symbol.name, symbol.name + ), symbol.line, symbol.column, - )) + )); } + + self.symbols.insert(symbol.name.clone(), symbol); + Ok(()) } pub fn resolve(&self, name: &str) -> Option<&Symbol> { @@ -244,6 +263,10 @@ impl Analyzer { } } + pub fn is_builtin_function(name: &str) -> bool { + crate::builtins::is_builtin_function(name) + } + pub fn analyze(&mut self, program: &Program) -> Result<(), Vec> { for statement in &program.statements { self.analyze_statement(statement); @@ -536,6 +559,9 @@ impl Analyzer { self.errors.push(error); } + // Add item to action_parameters to prevent it from being flagged as undefined + self.action_parameters.insert(item_name.clone()); + for stmt in body { self.analyze_statement(stmt); } @@ -1218,10 +1244,8 @@ impl Analyzer { return; } - // Special case for helper_function and nested_function - if name == "helper_function" || name == "nested_function" { - // Add these to action_parameters to prevent them from being flagged as undefined - self.action_parameters.insert(name.clone()); + // Check if it's a builtin function + if Self::is_builtin_function(name) { return; } diff --git a/src/builtins.rs b/src/builtins.rs new file mode 100644 index 00000000..c2d33e00 --- /dev/null +++ b/src/builtins.rs @@ -0,0 +1,246 @@ +//! Centralized registry of WFL builtin functions +//! This module ensures the Analyzer and TypeChecker remain synchronized + +use std::collections::HashSet; +use std::sync::OnceLock; + +/// Complete list of all builtin function names in WFL +/// This list includes: +/// 1. Functions actually implemented in stdlib modules +/// 2. Functions recognized by TypeChecker (for future compatibility) +/// 3. Special test functions used in test programs +const BUILTIN_FUNCTIONS: &[&str] = &[ + // Core functions (implemented in stdlib/core.rs) + "print", + "typeof", + "type_of", + "isnothing", + "is_nothing", + // Math functions (implemented in stdlib/math.rs) + "abs", + "round", + "floor", + "ceil", + "random", + "clamp", + // Math functions recognized by TypeChecker but not yet implemented + "min", + "max", + "power", + "sqrt", + "sin", + "cos", + "tan", + // Text functions (implemented in stdlib/text.rs) + "length", // Also works for lists + "touppercase", + "to_uppercase", + "tolowercase", + "to_lowercase", + "contains", // Also works for lists + "substring", + // Text functions recognized by TypeChecker but not yet implemented + "indexof", + "index_of", + "lastindexof", + "last_index_of", + "replace", + "trim", + "padleft", + "padright", + "capitalize", + "reverse", + "startswith", + "starts_with", + "endswith", + "ends_with", + "split", + "join", + // List functions (implemented in stdlib/list.rs) + "push", + "pop", + // List functions recognized by TypeChecker but not yet implemented + "shift", + "unshift", + "remove_at", + "removeat", + "insert_at", + "insertat", + "sort", + "reverse_list", + "filter", + "map", + "reduce", + "foreach", + "find", + "find_index", + "includes", + "slice", + "every", + "some", + "fill", + "concat", + "unique", + "clear", + "count", + "size", + // Time functions (implemented in stdlib/time.rs) + "now", + "today", + "datetime_now", + "format_date", + "format_time", + "format_datetime", + "parse_date", + "parse_time", + "create_time", + "create_date", + "add_days", + "days_between", + "current_date", + // Time functions recognized by TypeChecker but not yet implemented + "sleep", + "time", + "date", + "year", + "month", + "day", + "hour", + "minute", + "second", + "dayofweek", + "day_of_week", + "adddays", // Duplicate of add_days + "addmonths", + "add_months", + "addyears", + "add_years", + "addhours", + "add_hours", + "addminutes", + "add_minutes", + "addseconds", + "add_seconds", + "formatdate", // Duplicate of format_date + "formattime", // Duplicate of format_time + "parsedate", // Duplicate of parse_date + "isleapyear", + "is_leap_year", + "daysbetween", // Duplicate of days_between + "monthsbetween", + "months_between", + "yearsbetween", + "years_between", + // Pattern functions (implemented in stdlib/pattern.rs) + "pattern_matches", + "pattern_find", + "pattern_find_all", + // Pattern functions recognized by TypeChecker but not yet implemented + "compile_pattern", + "match_pattern", + "replace_pattern", + "pattern", + "match", + "test", + "extract", + "ismatch", + "is_match", + "findall", + "find_all", + // File system functions (implemented in stdlib/filesystem.rs) + "list_dir", + "glob", + "rglob", + "path_join", + "path_basename", + "path_dirname", + "makedirs", + "file_mtime", + "path_exists", + "is_file", + "is_dir", + // File system functions recognized by TypeChecker but not yet implemented + "read_file", + "write_file", + "file_exists", + "delete_file", + "create_directory", + "list_directory", + "is_directory", + // Special test functions (used in test programs) + "helper_function", + "nested_function", +]; + +/// Cached HashSet for O(1) lookup performance +static BUILTIN_SET: OnceLock> = OnceLock::new(); + +/// Initialize the builtin function set +fn get_builtin_set() -> &'static HashSet<&'static str> { + BUILTIN_SET.get_or_init(|| BUILTIN_FUNCTIONS.iter().copied().collect()) +} + +/// Check if a function name is a builtin +pub fn is_builtin_function(name: &str) -> bool { + get_builtin_set().contains(name) +} + +/// Get an iterator over all builtin function names +pub fn builtin_functions() -> impl Iterator { + BUILTIN_FUNCTIONS.iter().copied() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_builtin_function() { + // Core functions + assert!(is_builtin_function("print")); + assert!(is_builtin_function("typeof")); + assert!(is_builtin_function("type_of")); + + // Math functions + assert!(is_builtin_function("abs")); + assert!(is_builtin_function("min")); + assert!(is_builtin_function("max")); + assert!(is_builtin_function("sqrt")); + + // Text functions + assert!(is_builtin_function("length")); + assert!(is_builtin_function("substring")); + assert!(is_builtin_function("index_of")); + assert!(is_builtin_function("starts_with")); + + // List functions + assert!(is_builtin_function("push")); + assert!(is_builtin_function("pop")); + assert!(is_builtin_function("unique")); + assert!(is_builtin_function("clear")); + + // Time functions + assert!(is_builtin_function("now")); + assert!(is_builtin_function("today")); + assert!(is_builtin_function("year")); + + // Pattern functions + assert!(is_builtin_function("pattern")); + assert!(is_builtin_function("match")); + assert!(is_builtin_function("test")); + + // Non-builtins + assert!(!is_builtin_function("not_a_function")); + assert!(!is_builtin_function("random_name")); + } + + #[test] + fn test_no_duplicates() { + let set = get_builtin_set(); + assert_eq!( + set.len(), + BUILTIN_FUNCTIONS.len(), + "Duplicate builtin function names detected" + ); + } +} diff --git a/src/debug_report.rs b/src/debug_report.rs index ddb4bfef..9346ecdd 100644 --- a/src/debug_report.rs +++ b/src/debug_report.rs @@ -291,8 +291,8 @@ mod tests { { let mut env_mut = env.borrow_mut(); - env_mut.define("x", Value::Number(42.0)); - env_mut.define("y", Value::Text("hello".into())); + let _ = env_mut.define("x", Value::Number(42.0)); + let _ = env_mut.define("y", Value::Text("hello".into())); } call_frame.capture_locals(&env); diff --git a/src/interpreter/environment.rs b/src/interpreter/environment.rs index b4ab69e7..d7c36ba4 100644 --- a/src/interpreter/environment.rs +++ b/src/interpreter/environment.rs @@ -45,13 +45,49 @@ impl Environment { })) } - pub fn define(&mut self, name: &str, value: Value) { + pub fn define(&mut self, name: &str, value: Value) -> Result<(), String> { + // Check if the variable already exists in current scope + if self.values.contains_key(name) { + return Err(format!( + "Variable '{name}' has already been defined. Use 'change {name} to ' to modify it." + )); + } + + // Check if the variable exists in parent scopes + if let Some(parent_weak) = &self.parent + && let Some(parent) = parent_weak.upgrade() + && parent.borrow().get(name).is_some() + { + return Err(format!( + "Variable '{name}' has already been defined in an outer scope. Use 'change {name} to ' to modify it." + )); + } + self.values.insert(name.to_string(), value); + Ok(()) } - pub fn define_constant(&mut self, name: &str, value: Value) { + pub fn define_constant(&mut self, name: &str, value: Value) -> Result<(), String> { + // Check if the variable/constant already exists + if self.values.contains_key(name) { + return Err(format!( + "Variable or constant '{name}' has already been defined." + )); + } + + // Check if the variable exists in parent scopes + if let Some(parent_weak) = &self.parent + && let Some(parent) = parent_weak.upgrade() + && parent.borrow().get(name).is_some() + { + return Err(format!( + "Variable or constant '{name}' has already been defined in an outer scope." + )); + } + self.values.insert(name.to_string(), value); self.constants.insert(name.to_string()); + Ok(()) } pub fn is_constant(&self, name: &str) -> bool { diff --git a/src/interpreter/memory_tests.rs b/src/interpreter/memory_tests.rs index def4d023..02f9c802 100644 --- a/src/interpreter/memory_tests.rs +++ b/src/interpreter/memory_tests.rs @@ -27,7 +27,7 @@ mod tests { // Store in environment let function_value = Value::Function(Rc::new(function)); - global_env + let _ = global_env .borrow_mut() .define("test_function", function_value); @@ -118,7 +118,7 @@ mod tests { }; let function_value = Value::Function(Rc::new(function)); - child_env.borrow_mut().define(name, function_value); + let _ = child_env.borrow_mut().define(name, function_value); } // Ensure child_env is properly linked to its parent diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 8ce1af0e..c93135ac 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -460,7 +460,7 @@ impl Interpreter { { let mut env = global_env.borrow_mut(); - env.define( + let _ = env.define( "display", Value::NativeFunction("display", Self::native_display), ); @@ -642,7 +642,7 @@ impl Interpreter { .iter() .map(|arg| Value::Text(Rc::from(arg.as_str()))) .collect(); - env.define("args", Value::List(Rc::new(RefCell::new(args_list)))); + let _ = env.define("args", Value::List(Rc::new(RefCell::new(args_list)))); // Parse and set up flags (arguments starting with - or --) let mut flags = HashMap::new(); @@ -691,17 +691,17 @@ impl Interpreter { } // Store positional arguments - env.define( + let _ = env.define( "positional_args", Value::List(Rc::new(RefCell::new(positional_args.clone()))), ); // Store argument count - env.define("arg_count", Value::Number(self.script_args.len() as f64)); + let _ = env.define("arg_count", Value::Number(self.script_args.len() as f64)); // Store flags as individual variables with flag_ prefix for (key, value) in flags_map { - env.define(&format!("flag_{key}"), value); + let _ = env.define(&format!("flag_{key}"), value); } } @@ -905,13 +905,17 @@ impl Interpreter { #[cfg(debug_assertions)] exec_var_declare!(name, &evaluated_value); - if *is_constant { + let result = if *is_constant { env.borrow_mut() - .define_constant(name, evaluated_value.clone()); + .define_constant(name, evaluated_value.clone()) } else { - env.borrow_mut().define(name, evaluated_value.clone()); + env.borrow_mut().define(name, evaluated_value.clone()) + }; + + match result { + Ok(_) => Ok((Value::Null, ControlFlow::None)), + Err(msg) => Err(RuntimeError::new(msg, line, column)), } - Ok((Value::Null, ControlFlow::None)) } Statement::Assignment { @@ -1011,7 +1015,10 @@ impl Interpreter { }; let function_value = Value::Function(Rc::new(function)); - env.borrow_mut().define(name, function_value.clone()); + match env.borrow_mut().define(name, function_value.clone()) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } Ok((function_value, ControlFlow::None)) } @@ -1107,7 +1114,6 @@ impl Interpreter { }; let mut count = start_num; - let loop_env = Environment::new_child_env(&env); let should_continue: Box bool> = if *downward { Box::new(|count, end_num| count >= end_num) @@ -1129,9 +1135,12 @@ impl Interpreter { *self.current_count.borrow_mut() = Some(count); + // Create a new scope for each iteration + let loop_env = Environment::new_child_env(&env); + // Also make count available as a regular variable in the loop environment // This ensures consistency and allows for nested count loops to work properly - loop_env.borrow_mut().define("count", Value::Number(count)); + let _ = loop_env.borrow_mut().define("count", Value::Number(count)); let result = self.execute_block(body, Rc::clone(&loop_env)).await; @@ -1204,8 +1213,6 @@ impl Interpreter { .evaluate_expression(collection, Rc::clone(&env)) .await?; - let loop_env = Environment::new_child_env(&env); - match collection_val { Value::List(list_rc) => { let items: Vec = { @@ -1219,7 +1226,12 @@ impl Interpreter { }; for item in items { - loop_env.borrow_mut().define(item_name, item); + // Create a new scope for each iteration + let loop_env = Environment::new_child_env(&env); + match loop_env.borrow_mut().define(item_name, item) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } let result = self.execute_block(body, Rc::clone(&loop_env)).await?; match result.1 { @@ -1257,7 +1269,12 @@ impl Interpreter { }; for (_, value) in items { - loop_env.borrow_mut().define(item_name, value); + // Create a new scope for each iteration + let loop_env = Environment::new_child_env(&env); + match loop_env.borrow_mut().define(item_name, value) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } let result = self.execute_block(body, Rc::clone(&loop_env)).await?; match result.1 { @@ -1530,9 +1547,13 @@ impl Interpreter { .await { Ok(handle) => { - env.borrow_mut() - .define(variable_name, Value::Text(handle.into())); - Ok((Value::Null, ControlFlow::None)) + match env + .borrow_mut() + .define(variable_name, Value::Text(handle.into())) + { + Ok(_) => Ok((Value::Null, ControlFlow::None)), + Err(msg) => Err(RuntimeError::new(msg, *line, *column)), + } } Err(e) => Err(e), } @@ -1561,10 +1582,19 @@ impl Interpreter { match self.io_client.open_file(&path_str).await { Ok(handle) => match self.io_client.read_file(&handle).await { Ok(content) => { - env.borrow_mut() - .define(variable_name, Value::Text(content.into())); - let _ = self.io_client.close_file(&handle).await; - Ok((Value::Null, ControlFlow::None)) + match env + .borrow_mut() + .define(variable_name, Value::Text(content.into())) + { + Ok(_) => { + let _ = self.io_client.close_file(&handle).await; + Ok((Value::Null, ControlFlow::None)) + } + Err(msg) => { + let _ = self.io_client.close_file(&handle).await; + Err(RuntimeError::new(msg, *line, *column)) + } + } } Err(e) => { let _ = self.io_client.close_file(&handle).await; @@ -1576,9 +1606,13 @@ impl Interpreter { } else { match self.io_client.read_file(&path_str).await { Ok(content) => { - env.borrow_mut() - .define(variable_name, Value::Text(content.into())); - Ok((Value::Null, ControlFlow::None)) + match env + .borrow_mut() + .define(variable_name, Value::Text(content.into())) + { + Ok(_) => Ok((Value::Null, ControlFlow::None)), + Err(msg) => Err(RuntimeError::new(msg, *line, *column)), + } } Err(e) => Err(RuntimeError::new(e, *line, *column)), } @@ -1879,10 +1913,19 @@ impl Interpreter { match self.io_client.open_file(&path_str).await { Ok(handle) => match self.io_client.read_file(&handle).await { Ok(content) => { - env.borrow_mut() - .define(variable_name, Value::Text(content.into())); - let _ = self.io_client.close_file(&handle).await; - Ok((Value::Null, ControlFlow::None)) + match env + .borrow_mut() + .define(variable_name, Value::Text(content.into())) + { + Ok(_) => { + let _ = self.io_client.close_file(&handle).await; + Ok((Value::Null, ControlFlow::None)) + } + Err(msg) => { + let _ = self.io_client.close_file(&handle).await; + Err(RuntimeError::new(msg, *line, *column)) + } + } } Err(e) => { let _ = self.io_client.close_file(&handle).await; @@ -1894,9 +1937,13 @@ impl Interpreter { } else { match self.io_client.read_file(&path_str).await { Ok(content) => { - env.borrow_mut() - .define(variable_name, Value::Text(content.into())); - Ok((Value::Null, ControlFlow::None)) + match env + .borrow_mut() + .define(variable_name, Value::Text(content.into())) + { + Ok(_) => Ok((Value::Null, ControlFlow::None)), + Err(msg) => Err(RuntimeError::new(msg, *line, *column)), + } } Err(e) => Err(RuntimeError::new(e, *line, *column)), } @@ -1933,7 +1980,7 @@ impl Interpreter { }; if matches { - child_env.borrow_mut().define( + let _ = child_env.borrow_mut().define( &when_clause.error_name, Value::Text(err.message.into()), ); @@ -1977,9 +2024,13 @@ impl Interpreter { match self.io_client.http_get(&url_str).await { Ok(body) => { - env.borrow_mut() - .define(variable_name, Value::Text(body.into())); - Ok((Value::Null, ControlFlow::None)) + match env + .borrow_mut() + .define(variable_name, Value::Text(body.into())) + { + Ok(_) => Ok((Value::Null, ControlFlow::None)), + Err(msg) => Err(RuntimeError::new(msg, *line, *column)), + } } Err(e) => Err(RuntimeError::new(e, *line, *column)), } @@ -2018,9 +2069,13 @@ impl Interpreter { match self.io_client.http_post(&url_str, &data_str).await { Ok(body) => { - env.borrow_mut() - .define(variable_name, Value::Text(body.into())); - Ok((Value::Null, ControlFlow::None)) + match env + .borrow_mut() + .define(variable_name, Value::Text(body.into())) + { + Ok(_) => Ok((Value::Null, ControlFlow::None)), + Err(msg) => Err(RuntimeError::new(msg, *line, *column)), + } } Err(e) => Err(RuntimeError::new(e, *line, *column)), } @@ -2076,8 +2131,8 @@ impl Interpreter { Statement::CreateListStatement { name, initial_values, - line: _, - column: _, + line, + column, } => { // Create a new list with initial values let mut list_items = Vec::new(); @@ -2089,15 +2144,18 @@ impl Interpreter { } let list_value = Value::List(Rc::new(RefCell::new(list_items))); - env.borrow_mut().define(name, list_value); + match env.borrow_mut().define(name, list_value) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } Ok((Value::Null, ControlFlow::None)) } Statement::MapCreation { name, entries, - line: _, - column: _, + line, + column, } => { // Create a new map/object with initial entries let mut map = std::collections::HashMap::new(); @@ -2109,15 +2167,18 @@ impl Interpreter { } let map_value = Value::Object(Rc::new(RefCell::new(map))); - env.borrow_mut().define(name, map_value); + match env.borrow_mut().define(name, map_value) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } Ok((Value::Null, ControlFlow::None)) } Statement::CreateDateStatement { name, value, - line: _, - column: _, + line, + column, } => { let date_value = if let Some(expr) = value { // Evaluate the expression to get the date @@ -2128,14 +2189,17 @@ impl Interpreter { Value::Date(Rc::new(today)) }; - env.borrow_mut().define(name, date_value); + match env.borrow_mut().define(name, date_value) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } Ok((Value::Null, ControlFlow::None)) } Statement::CreateTimeStatement { name, value, - line: _, - column: _, + line, + column, } => { let time_value = if let Some(expr) = value { // Evaluate the expression to get the time @@ -2146,7 +2210,10 @@ impl Interpreter { Value::Time(Rc::new(now)) }; - env.borrow_mut().define(name, time_value); + match env.borrow_mut().define(name, time_value) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } Ok((Value::Null, ControlFlow::None)) } Statement::AddToListStatement { @@ -2330,7 +2397,10 @@ impl Interpreter { let container_value = Value::ContainerDefinition(Rc::new(container_def)); // Store the container definition in the environment - env.borrow_mut().define(name, container_value.clone()); + match env.borrow_mut().define(name, container_value.clone()) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } Ok((container_value, ControlFlow::None)) } @@ -2363,8 +2433,13 @@ impl Interpreter { let instance_value = Value::ContainerInstance(Rc::new(RefCell::new(instance))); // Store the instance in the environment - env.borrow_mut() - .define(instance_name, instance_value.clone()); + match env + .borrow_mut() + .define(instance_name, instance_value.clone()) + { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } // Call constructor method if arguments are provided if !arguments.is_empty() { @@ -2396,7 +2471,7 @@ impl Interpreter { let init_env = Environment::new_child_env(&env); // Add 'this' to the environment (the instance being constructed) - init_env.borrow_mut().define("this", instance_value.clone()); + let _ = init_env.borrow_mut().define("this", instance_value.clone()); // Evaluate the arguments let mut arg_values = Vec::with_capacity(arguments.len()); @@ -2452,7 +2527,10 @@ impl Interpreter { let interface_value = Value::InterfaceDefinition(Rc::new(interface_def)); // Store the interface definition in the environment - env.borrow_mut().define(name, interface_value.clone()); + match env.borrow_mut().define(name, interface_value.clone()) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *_line, *_column)), + } Ok((interface_value, ControlFlow::None)) } @@ -2474,7 +2552,10 @@ impl Interpreter { let event_value = Value::ContainerEvent(Rc::new(event_def)); // Store the event definition in the environment - env.borrow_mut().define(name, event_value.clone()); + match env.borrow_mut().define(name, event_value.clone()) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *_line, *_column)), + } Ok((event_value, ControlFlow::None)) } @@ -2513,11 +2594,11 @@ impl Interpreter { // Bind arguments to parameters for (i, param_name) in event.params.iter().enumerate() { if i < arg_values.len() { - handler_env + let _ = handler_env .borrow_mut() .define(param_name, arg_values[i].clone()); } else { - handler_env.borrow_mut().define(param_name, Value::Null); + let _ = handler_env.borrow_mut().define(param_name, Value::Null); } } @@ -2581,7 +2662,7 @@ impl Interpreter { // Store the updated event in the environment let event_value = Value::ContainerEvent(Rc::new(new_event)); - env.borrow_mut().define(event_name, event_value.clone()); + let _ = env.borrow_mut().define(event_name, event_value.clone()); Ok((Value::Null, ControlFlow::None)) } else { @@ -2657,7 +2738,7 @@ impl Interpreter { let method_env = Environment::new_child_env(&env); // Add 'this' to the environment (the current instance, not the parent) - method_env.borrow_mut().define("this", this_val.clone()); + let _ = method_env.borrow_mut().define("this", this_val.clone()); // Evaluate the arguments let mut arg_values = Vec::with_capacity(arguments.len()); @@ -2698,20 +2779,29 @@ impl Interpreter { )) } } - Statement::PatternDefinition { name, pattern, .. } => { + Statement::PatternDefinition { + name, + pattern, + line, + column, + .. + } => { // Compile the pattern AST into bytecode match CompiledPattern::compile(pattern) { Ok(compiled_pattern) => { // Store the compiled pattern in the environment let pattern_value = Value::Pattern(Rc::new(compiled_pattern)); - env.borrow_mut().define(name, pattern_value.clone()); + match env.borrow_mut().define(name, pattern_value.clone()) { + Ok(_) => {} + Err(msg) => return Err(RuntimeError::new(msg, *line, *column)), + } Ok((pattern_value, ControlFlow::None)) } Err(compile_error) => Err(RuntimeError { kind: ErrorKind::General, message: format!("Failed to compile pattern '{name}': {compile_error}"), - line, - column, + line: *line, + column: *column, }), } } @@ -2878,7 +2968,7 @@ impl Interpreter { let method_env = Environment::new_child_env(&env); // Add 'this' to the environment - method_env.borrow_mut().define("this", object_val.clone()); + let _ = method_env.borrow_mut().define("this", object_val.clone()); // Evaluate the arguments let mut arg_values = Vec::with_capacity(arguments.len()); @@ -3680,7 +3770,7 @@ impl Interpreter { #[cfg(debug_assertions)] exec_var_declare!(param, &arg); - call_env.borrow_mut().define(param, arg.clone()); + let _ = call_env.borrow_mut().define(param, arg.clone()); } let frame = CallFrame::new( diff --git a/src/lexer/token.rs b/src/lexer/token.rs index 86d27028..12ca53c2 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -291,6 +291,9 @@ pub enum Token { #[token("+")] Plus, + #[token("-")] + Minus, + #[token(".")] Dot, diff --git a/src/lib.rs b/src/lib.rs index f65859b2..a7bc06f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ static ALLOC: dhat::Alloc = dhat::Alloc; pub mod analyzer; +pub mod builtins; pub mod config; pub mod debug_report; pub mod diagnostics; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ee7dcf3c..389474c9 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1474,6 +1474,7 @@ impl<'a> Parser<'a> { let op = match token { Token::Plus => Some((Operator::Plus, 1)), Token::KeywordPlus => Some((Operator::Plus, 1)), + Token::Minus => Some((Operator::Minus, 1)), Token::KeywordMinus => Some((Operator::Minus, 1)), Token::KeywordTimes => Some((Operator::Multiply, 2)), Token::KeywordDividedBy => Some((Operator::Divide, 2)), @@ -1871,6 +1872,9 @@ impl<'a> Parser<'a> { Token::KeywordMinus => { self.tokens.next(); // Consume "minus" } + Token::Minus => { + self.tokens.next(); // Consume "-" + } Token::KeywordTimes => { self.tokens.next(); // Consume "times" } @@ -2158,6 +2162,18 @@ impl<'a> Parser<'a> { column: token_column, }) } + Token::Minus => { + self.tokens.next(); // Consume "-" + let expr = self.parse_primary_expression()?; + let token_line = token.line; + let token_column = token.column; + Ok(Expression::UnaryOperation { + operator: UnaryOperator::Minus, + expression: Box::new(expr), + line: token_line, + column: token_column, + }) + } Token::KeywordWith => { self.tokens.next(); // Consume "with" let expr = self.parse_expression()?; @@ -2542,7 +2558,8 @@ impl<'a> Parser<'a> { self.tokens.next(); // Consume "of" // Parse the first argument after "of" - let first_arg = self.parse_expression()?; + // Use parse_primary_expression to avoid treating "and" as a binary operator + let first_arg = self.parse_primary_expression()?; let is_function_call = matches!( expr, @@ -2561,7 +2578,8 @@ impl<'a> Parser<'a> { if let Token::KeywordAnd = &and_token.token { self.tokens.next(); // Consume "and" - let arg_value = self.parse_expression()?; + // Use parse_primary_expression to avoid treating next "and" as binary operator + let arg_value = self.parse_primary_expression()?; arguments.push(Argument { name: None, diff --git a/src/parser/tests.rs b/src/parser/tests.rs index ea3881e2..64ae099b 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -729,3 +729,179 @@ fn debug_token_sequence() { println!("{i}: {token:?}"); } } + +#[test] +fn test_subtraction_basic() { + let input = "display 5 - 3"; + let tokens = lex_wfl_with_positions(input); + let mut parser = Parser::new(&tokens); + + let result = parser.parse_statement(); + assert!( + result.is_ok(), + "Failed to parse 'display 5 - 3': {result:?}" + ); + + if let Ok(Statement::DisplayStatement { value, .. }) = result { + if let Expression::BinaryOperation { + left, + operator, + right, + .. + } = value + { + if let Expression::Literal(Literal::Integer(n), ..) = *left { + assert_eq!(n, 5, "Expected left operand to be 5"); + } else { + panic!("Expected integer literal 5, got: {left:?}"); + } + + assert_eq!(operator, Operator::Minus, "Expected Minus operator"); + + if let Expression::Literal(Literal::Integer(n), ..) = *right { + assert_eq!(n, 3, "Expected right operand to be 3"); + } else { + panic!("Expected integer literal 3, got: {right:?}"); + } + } else { + panic!("Expected binary operation, got: {value:?}"); + } + } else { + panic!("Expected display statement, got: {result:?}"); + } +} + +#[test] +fn test_subtraction_with_negative() { + let input = "display 5 - -3"; + let tokens = lex_wfl_with_positions(input); + let mut parser = Parser::new(&tokens); + + let result = parser.parse_statement(); + assert!( + result.is_ok(), + "Failed to parse 'display 5 - -3': {result:?}" + ); + + if let Ok(Statement::DisplayStatement { value, .. }) = result { + if let Expression::BinaryOperation { + left, + operator, + right, + .. + } = value + { + if let Expression::Literal(Literal::Integer(n), ..) = *left { + assert_eq!(n, 5, "Expected left operand to be 5"); + } else { + panic!("Expected integer literal 5, got: {left:?}"); + } + + assert_eq!(operator, Operator::Minus, "Expected Minus operator"); + + // Right side should be unary minus with 3 + if let Expression::UnaryOperation { + operator: unary_op, + expression, + .. + } = *right + { + assert_eq!( + unary_op, + UnaryOperator::Minus, + "Expected unary minus operator" + ); + + if let Expression::Literal(Literal::Integer(n), ..) = *expression { + assert_eq!(n, 3, "Expected operand to be 3"); + } else { + panic!("Expected integer literal 3, got: {expression:?}"); + } + } else { + panic!("Expected unary operation, got: {right:?}"); + } + } else { + panic!("Expected binary operation, got: {value:?}"); + } + } else { + panic!("Expected display statement, got: {result:?}"); + } +} + +#[test] +fn test_unary_minus_with_complex_expression() { + let input = "display -(1 + 2) times 3"; + let tokens = lex_wfl_with_positions(input); + let mut parser = Parser::new(&tokens); + + let result = parser.parse_statement(); + assert!( + result.is_ok(), + "Failed to parse 'display -(1 + 2) times 3': {result:?}" + ); + + if let Ok(Statement::DisplayStatement { value, .. }) = result { + // Should be: (-(1 + 2)) * 3 + if let Expression::BinaryOperation { + left, + operator, + right, + .. + } = value + { + assert_eq!(operator, Operator::Multiply, "Expected Multiply operator"); + + // Left side should be unary minus with (1 + 2) + if let Expression::UnaryOperation { + operator: unary_op, + expression, + .. + } = *left + { + assert_eq!( + unary_op, + UnaryOperator::Minus, + "Expected unary minus operator" + ); + + // expression should be (1 + 2) + if let Expression::BinaryOperation { + left: inner_left, + operator: inner_op, + right: inner_right, + .. + } = *expression + { + assert_eq!(inner_op, Operator::Plus, "Expected Plus operator"); + + if let Expression::Literal(Literal::Integer(n), ..) = *inner_left { + assert_eq!(n, 1, "Expected left operand to be 1"); + } else { + panic!("Expected integer literal 1, got: {inner_left:?}"); + } + + if let Expression::Literal(Literal::Integer(n), ..) = *inner_right { + assert_eq!(n, 2, "Expected right operand to be 2"); + } else { + panic!("Expected integer literal 2, got: {inner_right:?}"); + } + } else { + panic!("Expected binary operation (1 + 2), got: {expression:?}"); + } + } else { + panic!("Expected unary operation, got: {left:?}"); + } + + // Right side should be 3 + if let Expression::Literal(Literal::Integer(n), ..) = *right { + assert_eq!(n, 3, "Expected right operand to be 3"); + } else { + panic!("Expected integer literal 3, got: {right:?}"); + } + } else { + panic!("Expected binary operation, got: {value:?}"); + } + } else { + panic!("Expected display statement, got: {result:?}"); + } +} diff --git a/src/stdlib/core.rs b/src/stdlib/core.rs index 43ce56df..2abefacf 100644 --- a/src/stdlib/core.rs +++ b/src/stdlib/core.rs @@ -43,16 +43,16 @@ pub fn native_isnothing(args: Vec) -> Result { } pub fn register_core(env: &mut Environment) { - env.define("print", Value::NativeFunction("print", native_print)); + let _ = env.define("print", Value::NativeFunction("print", native_print)); - env.define("typeof", Value::NativeFunction("typeof", native_typeof)); - env.define( + let _ = env.define("typeof", Value::NativeFunction("typeof", native_typeof)); + let _ = env.define( "isnothing", Value::NativeFunction("isnothing", native_isnothing), ); - env.define("type_of", Value::NativeFunction("type_of", native_typeof)); - env.define( + let _ = env.define("type_of", Value::NativeFunction("type_of", native_typeof)); + let _ = env.define( "is_nothing", Value::NativeFunction("is_nothing", native_isnothing), ); diff --git a/src/stdlib/filesystem.rs b/src/stdlib/filesystem.rs index aa28ca08..bc04038c 100644 --- a/src/stdlib/filesystem.rs +++ b/src/stdlib/filesystem.rs @@ -311,38 +311,38 @@ pub fn native_is_dir(args: Vec) -> Result { } pub fn register_filesystem(env: &mut crate::interpreter::environment::Environment) { - env.define( + let _ = env.define( "list_dir", Value::NativeFunction("list_dir", native_list_dir), ); - env.define("glob", Value::NativeFunction("glob", native_glob)); - env.define("rglob", Value::NativeFunction("rglob", native_rglob)); - env.define( + let _ = env.define("glob", Value::NativeFunction("glob", native_glob)); + let _ = env.define("rglob", Value::NativeFunction("rglob", native_rglob)); + let _ = env.define( "path_join", Value::NativeFunction("path_join", native_path_join), ); - env.define( + let _ = env.define( "path_basename", Value::NativeFunction("path_basename", native_path_basename), ); - env.define( + let _ = env.define( "path_dirname", Value::NativeFunction("path_dirname", native_path_dirname), ); - env.define( + let _ = env.define( "makedirs", Value::NativeFunction("makedirs", native_makedirs), ); - env.define( + let _ = env.define( "file_mtime", Value::NativeFunction("file_mtime", native_file_mtime), ); - env.define( + let _ = env.define( "path_exists", Value::NativeFunction("path_exists", native_path_exists), ); - env.define("is_file", Value::NativeFunction("is_file", native_is_file)); - env.define("is_dir", Value::NativeFunction("is_dir", native_is_dir)); + let _ = env.define("is_file", Value::NativeFunction("is_file", native_is_file)); + let _ = env.define("is_dir", Value::NativeFunction("is_dir", native_is_dir)); } #[cfg(test)] diff --git a/src/stdlib/list.rs b/src/stdlib/list.rs index 2ecc4ae9..644bae5c 100644 --- a/src/stdlib/list.rs +++ b/src/stdlib/list.rs @@ -129,16 +129,16 @@ pub fn native_indexof(args: Vec) -> Result { } pub fn register_list(env: &mut Environment) { - env.define("length", Value::NativeFunction("length", native_length)); - env.define("push", Value::NativeFunction("push", native_push)); - env.define("pop", Value::NativeFunction("pop", native_pop)); - env.define( + let _ = env.define("length", Value::NativeFunction("length", native_length)); + let _ = env.define("push", Value::NativeFunction("push", native_push)); + let _ = env.define("pop", Value::NativeFunction("pop", native_pop)); + let _ = env.define( "contains", Value::NativeFunction("contains", native_contains), ); - env.define("indexof", Value::NativeFunction("indexof", native_indexof)); + let _ = env.define("indexof", Value::NativeFunction("indexof", native_indexof)); - env.define( + let _ = env.define( "index_of", Value::NativeFunction("index_of", native_indexof), ); diff --git a/src/stdlib/math.rs b/src/stdlib/math.rs index ca12d404..8e54e5c1 100644 --- a/src/stdlib/math.rs +++ b/src/stdlib/math.rs @@ -111,10 +111,10 @@ pub fn native_clamp(args: Vec) -> Result { } pub fn register_math(env: &mut Environment) { - env.define("abs", Value::NativeFunction("abs", native_abs)); - env.define("round", Value::NativeFunction("round", native_round)); - env.define("floor", Value::NativeFunction("floor", native_floor)); - env.define("ceil", Value::NativeFunction("ceil", native_ceil)); - env.define("random", Value::NativeFunction("random", native_random)); - env.define("clamp", Value::NativeFunction("clamp", native_clamp)); + let _ = env.define("abs", Value::NativeFunction("abs", native_abs)); + let _ = env.define("round", Value::NativeFunction("round", native_round)); + let _ = env.define("floor", Value::NativeFunction("floor", native_floor)); + let _ = env.define("ceil", Value::NativeFunction("ceil", native_ceil)); + let _ = env.define("random", Value::NativeFunction("random", native_random)); + let _ = env.define("clamp", Value::NativeFunction("clamp", native_clamp)); } diff --git a/src/stdlib/pattern.rs b/src/stdlib/pattern.rs index ee65a9bc..440e93f9 100644 --- a/src/stdlib/pattern.rs +++ b/src/stdlib/pattern.rs @@ -7,15 +7,15 @@ use std::rc::Rc; pub fn register(env: &mut Environment) { // Register new pattern functions that work with our pattern system - env.define( + let _ = env.define( "pattern_matches", Value::NativeFunction("pattern_matches", pattern_matches_native), ); - env.define( + let _ = env.define( "pattern_find", Value::NativeFunction("pattern_find", pattern_find_native), ); - env.define( + let _ = env.define( "pattern_find_all", Value::NativeFunction("pattern_find_all", pattern_find_all_native), ); diff --git a/src/stdlib/text.rs b/src/stdlib/text.rs index 59f9174e..d64883ee 100644 --- a/src/stdlib/text.rs +++ b/src/stdlib/text.rs @@ -25,18 +25,8 @@ fn expect_number(value: &Value) -> Result { } } -pub fn native_length(args: Vec) -> Result { - if args.len() != 1 { - return Err(RuntimeError::new( - format!("length expects 1 argument, got {}", args.len()), - 0, - 0, - )); - } - - let text = expect_text(&args[0])?; - Ok(Value::Number(text.len() as f64)) -} +// Note: The length function is now provided by the list module +// which handles both text and lists pub fn native_touppercase(args: Vec) -> Result { if args.len() != 1 { @@ -108,29 +98,29 @@ pub fn native_substring(args: Vec) -> Result { } pub fn register_text(env: &mut Environment) { - env.define("length", Value::NativeFunction("length", native_length)); - env.define( + // Note: length function is registered by the list module instead + let _ = env.define( "touppercase", Value::NativeFunction("touppercase", native_touppercase), ); - env.define( + let _ = env.define( "tolowercase", Value::NativeFunction("tolowercase", native_tolowercase), ); - env.define( + let _ = env.define( "contains", Value::NativeFunction("contains", native_contains), ); - env.define( + let _ = env.define( "substring", Value::NativeFunction("substring", native_substring), ); - env.define( + let _ = env.define( "to_uppercase", Value::NativeFunction("to_uppercase", native_touppercase), ); - env.define( + let _ = env.define( "to_lowercase", Value::NativeFunction("to_lowercase", native_tolowercase), ); diff --git a/src/stdlib/time.rs b/src/stdlib/time.rs index 60a35852..8191aa6d 100644 --- a/src/stdlib/time.rs +++ b/src/stdlib/time.rs @@ -544,49 +544,49 @@ pub fn native_current_date(args: Vec) -> Result { /// Register all time-related functions in the environment pub fn register_time(env: &mut Environment) { - env.define("today", Value::NativeFunction("today", native_today)); - env.define("now", Value::NativeFunction("now", native_now)); - env.define( + let _ = env.define("today", Value::NativeFunction("today", native_today)); + let _ = env.define("now", Value::NativeFunction("now", native_now)); + let _ = env.define( "datetime_now", Value::NativeFunction("datetime_now", native_datetime_now), ); - env.define( + let _ = env.define( "format_date", Value::NativeFunction("format_date", native_format_date), ); - env.define( + let _ = env.define( "format_time", Value::NativeFunction("format_time", native_format_time), ); - env.define( + let _ = env.define( "format_datetime", Value::NativeFunction("format_datetime", native_format_datetime), ); - env.define( + let _ = env.define( "parse_date", Value::NativeFunction("parse_date", native_parse_date), ); - env.define( + let _ = env.define( "parse_time", Value::NativeFunction("parse_time", native_parse_time), ); - env.define( + let _ = env.define( "create_time", Value::NativeFunction("create_time", native_create_time), ); - env.define( + let _ = env.define( "create_date", Value::NativeFunction("create_date", native_create_date), ); - env.define( + let _ = env.define( "add_days", Value::NativeFunction("add_days", native_add_days), ); - env.define( + let _ = env.define( "days_between", Value::NativeFunction("days_between", native_days_between), ); - env.define( + let _ = env.define( "current_date", Value::NativeFunction("current_date", native_current_date), ); diff --git a/src/typechecker/mod.rs b/src/typechecker/mod.rs index 4854ed83..79b2c5c4 100644 --- a/src/typechecker/mod.rs +++ b/src/typechecker/mod.rs @@ -123,6 +123,52 @@ impl TypeChecker { self.analyzer.get_action_parameters() } + /// Get the return type for builtin functions + fn get_builtin_function_type(&self, name: &str, _arg_count: usize) -> Type { + match name { + // Type functions + "typeof" | "type_of" => Type::Text, + "isnothing" | "is_nothing" => Type::Boolean, + + // Math functions + "abs" | "round" | "floor" | "ceil" | "random" | "clamp" | "min" | "max" | "power" + | "sqrt" | "sin" | "cos" | "tan" => Type::Number, + + // Text functions + "length" | "indexof" | "index_of" | "lastindexof" | "last_index_of" => Type::Number, + "touppercase" | "tolowercase" | "substring" | "replace" | "trim" | "padleft" + | "padright" | "capitalize" | "reverse" => Type::Text, + "contains" | "startswith" | "starts_with" | "endswith" | "ends_with" => Type::Boolean, + "split" => Type::List(Box::new(Type::Text)), + "join" => Type::Text, + + // List functions + "push" | "pop" | "shift" | "unshift" | "removeat" | "remove_at" | "insertat" + | "insert_at" | "slice" | "concat" | "unique" | "sort" | "reverse_list" | "clear" + | "filter" | "map" => Type::List(Box::new(Type::Any)), + "find" => Type::Any, + "count" | "size" => Type::Number, + "includes" => Type::Boolean, + + // Time functions + "now" | "today" | "time" | "date" | "year" | "month" | "day" | "hour" | "minute" + | "second" | "dayofweek" | "day_of_week" | "adddays" | "add_days" | "addmonths" + | "add_months" | "addyears" | "add_years" | "addhours" | "add_hours" | "addminutes" + | "add_minutes" | "addseconds" | "add_seconds" => Type::Number, + "formatdate" | "format_date" | "formattime" | "format_time" => Type::Text, + "parsedate" | "parse_date" | "isleapyear" | "is_leap_year" => Type::Number, + "daysbetween" | "days_between" | "monthsbetween" | "months_between" + | "yearsbetween" | "years_between" => Type::Number, + + // Pattern functions + "pattern" | "match" | "test" | "replace_pattern" | "extract" => Type::Text, + "ismatch" | "is_match" => Type::Boolean, + "findall" | "find_all" => Type::List(Box::new(Type::Text)), + + _ => Type::Unknown, + } + } + pub fn check_types(&mut self, program: &Program) -> Result<(), Vec> { // Only run the analyzer if it hasn't been run already // When created with with_analyzer(), the analyzer has already been run, @@ -1167,8 +1213,9 @@ impl TypeChecker { Type::Unknown } } else { - // Check if this is an action parameter or a special function name before reporting it as undefined + // Check if this is an action parameter, builtin function, or special function name before reporting it as undefined if self.analyzer.get_action_parameters().contains(name) + || Analyzer::is_builtin_function(name) || name == "helper_function" || name == "nested_function" { @@ -1179,15 +1226,9 @@ impl TypeChecker { } Type::Unknown } else { - // Add an error for undefined variable - self.type_error( - format!("Variable '{name}' is not defined"), - None, - None, - *_line, - *_column, - ); - Type::Error + // The analyzer already reports undefined variables, so we don't need to duplicate the error + // Return Unknown type to continue type checking without cascading errors + Type::Unknown } } } @@ -1604,24 +1645,9 @@ impl TypeChecker { return Type::Error; } - if (left_type == Type::Text || left_type == Type::Number) - && (right_type == Type::Text || right_type == Type::Number) - { - Type::Text - } else { - self.type_error( - format!("Cannot concatenate {left_type} and {right_type}"), - Some(Type::Text), - Some(if left_type != Type::Text && left_type != Type::Number { - left_type - } else { - right_type - }), - *_line, - *_column, - ); - Type::Error - } + // Allow concatenation of any types - they will be converted to text at runtime + // This matches the interpreter's behavior which converts values to strings + Type::Text } Expression::PatternMatch { text, pattern, .. } => { let text_type = self.infer_expression_type(text); @@ -1773,12 +1799,17 @@ impl TypeChecker { let symbol_opt = self.analyzer.get_symbol(name); if symbol_opt.is_none() { - // Check if this is an action parameter or a special function name before reporting it as undefined + // Check if this is an action parameter, builtin function, or special function name before reporting it as undefined if self.analyzer.get_action_parameters().contains(name) + || Analyzer::is_builtin_function(name) || name == "helper_function" || name == "nested_function" { // It's an action parameter or a special function name, so don't report an error + // For builtin functions, return their proper type + if Analyzer::is_builtin_function(name) { + return self.get_builtin_function_type(name, arguments.len()); + } return Type::Unknown; } else { self.type_error( diff --git a/test.wfl b/test.wfl deleted file mode 100644 index 09db1e0b..00000000 --- a/test.wfl +++ /dev/null @@ -1,23 +0,0 @@ -// Nexus WFL Integration Test Script -// This script ("nexus.wfl") performs integration tests of core WFL features. -// It logs progress and results to "nexus.log" for debugging. - -/////////////////////////////////////////////////////////////////////////// -// 1. Setup: Initialize logging -/////////////////////////////////////////////////////////////////////////// - -// Open the log file (will be truncated/created anew) -open file at "nexus.log" as logHandle - -// Helper: Append a message line to the log file (read current content, add message, write back) -define action called log_message needs message_text: - // Read current log content - wait for open file at "nexus.log" and read content as currentLog - // Append new message (with newline) to current content - store updatedLog as currentLog with message_text with "\n" - // Write updated content back to log file - wait for write content updatedLog into logHandle -end action - -// Log the start of the test suite -log_message with "Starting Nexus WFL Integration Test Suite..." \ No newline at end of file diff --git a/test_chained_operations.wfl b/test_chained_operations.wfl deleted file mode 100644 index 35b5dec4..00000000 --- a/test_chained_operations.wfl +++ /dev/null @@ -1,26 +0,0 @@ -// Test chained binary operations bug -store a as 5 -store b as 10 -store c as 15 - -// This should be 30 (5 + 10 + 15) but currently returns only 15 -store result as a plus b plus c -display result - -// String concatenation test -store x as "hello" -store y as " " -store z as "world" - -// This should be "hello world" but currently returns only "world" -store greeting as x plus y plus z -display greeting - -// More complex test with mixed operations -store num1 as 2 -store num2 as 3 -store num3 as 4 - -// Should be 14 (2 * 3 + 4 + 2) but likely returns wrong value -store complex as num1 times num2 plus num3 plus num1 -display complex \ No newline at end of file diff --git a/test_pattern.wfl b/test_pattern.wfl deleted file mode 100644 index 9e798be0..00000000 --- a/test_pattern.wfl +++ /dev/null @@ -1 +0,0 @@ -create pattern test: "wfl" end pattern display "Pattern created" diff --git a/test_simple_pattern.wfl b/test_simple_pattern.wfl deleted file mode 100644 index d6ba1f96..00000000 --- a/test_simple_pattern.wfl +++ /dev/null @@ -1,32 +0,0 @@ -// Simple test of pattern creation and matching -create pattern test_pattern: - "hello" -end pattern - -display "Pattern created successfully" - -// Test positive case - should match -store test_text1 as "hello world" -check if test_text1 matches test_pattern: - display "✓ PASS: 'hello world' correctly matched the pattern" -otherwise: - display "✗ FAIL: 'hello world' should have matched the pattern" -end check - -// Test negative case - should not match -store test_text2 as "goodbye world" -check if test_text2 matches test_pattern: - display "✗ FAIL: 'goodbye world' should not have matched the pattern" -otherwise: - display "✓ PASS: 'goodbye world' correctly did not match the pattern" -end check - -// Test exact match -store test_text3 as "hello" -check if test_text3 matches test_pattern: - display "✓ PASS: Exact match 'hello' worked correctly" -otherwise: - display "✗ FAIL: Exact match 'hello' should have worked" -end check - -display "Pattern matching tests completed!" \ No newline at end of file diff --git a/tests/action_tests.rs b/tests/action_tests.rs deleted file mode 100644 index 5e6d2d3c..00000000 --- a/tests/action_tests.rs +++ /dev/null @@ -1,109 +0,0 @@ -use wfl::interpreter::Interpreter; -use wfl::interpreter::value::Value; -use wfl::lexer::lex_wfl_with_positions; -use wfl::parser::Parser; -use wfl::parser::ast::{Expression, Literal, Statement}; - -#[test] -fn test_action_def_parses() { - let source = "define action called log_message needs message_text: - display message_text - end action"; - - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 1); - match &program.statements[0] { - Statement::ActionDefinition { - name, parameters, .. - } => { - assert_eq!(name, "log_message"); - assert_eq!(parameters.len(), 1); - assert_eq!(parameters[0].name, "message_text"); - } - _ => panic!("Expected ActionDefinition, got {:?}", program.statements[0]), - } -} - -#[test] -fn test_action_call_parses() { - // Define the action first, then call it - let source = "define action called log_message needs message_text: - display message_text - end action - - log_message with \"Hello, world!\""; - - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 2); - match &program.statements[1] { - Statement::ExpressionStatement { expression, .. } => match expression { - Expression::ActionCall { - name, arguments, .. - } => { - assert_eq!(name, "log_message"); - assert_eq!(arguments.len(), 1); - match &arguments[0].value { - Expression::Literal(Literal::String(value), ..) => { - assert_eq!(value, "Hello, world!"); - } - _ => panic!("Expected string literal, got {:?}", arguments[0].value), - } - } - _ => panic!("Expected ActionCall, got {expression:?}"), - }, - _ => panic!( - "Expected ExpressionStatement, got {:?}", - program.statements[1] - ), - } -} - -#[test] -fn test_parser_token_consumption() { - let source = "define action called log_message needs message_text: - display message_text - end action - - log_message with \"test\""; - - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let result = parser.parse(); - - assert!(result.is_ok(), "Parser should not go into an infinite loop"); - let program = result.unwrap(); - assert_eq!(program.statements.len(), 2); -} - -#[tokio::test] -async fn test_action_call_executes() { - let source = " - define action called test_action needs param: - param - end action - - store result as test_action with 42 - "; - - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - assert!(result.is_ok(), "Failed to execute program: {result:?}"); - - let env = interpreter.global_env(); - let result_value = env.borrow().get("result").expect("Result not found"); - - match result_value { - Value::Number(n) => assert_eq!(n, 42.0), - _ => panic!("Expected number, got {result_value:?}"), - } -} diff --git a/tests/cli_tests.rs b/tests/cli_tests.rs deleted file mode 100644 index a0f2cec1..00000000 --- a/tests/cli_tests.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::fs; -use std::process::Command; -use tempfile::tempdir; - -#[test] -fn test_lint_fix_diff_combined() { - // Create a temporary directory for the test - let temp_dir = tempdir().expect("Failed to create temp directory"); - let file_path = temp_dir.path().join("test_file.wfl"); - - // Create a test file with a style issue (camelCase variable name) - let test_content = r#" -store myVariable as 42 -display myVariable -"#; - - fs::write(&file_path, test_content).expect("Failed to write test file"); - - assert!( - Command::new("cargo") - .args(["build"]) - .status() - .expect("Failed to build binary") - .success(), - "Failed to build the binary" - ); - - let binary_path = std::env::current_dir() - .expect("Failed to get current directory") - .join("target/debug/wfl"); - - // Run the binary with --lint file_path --fix --diff - let file_path_str = file_path.to_str().unwrap(); - println!("Running: {binary_path:?} --lint {file_path_str} --fix {file_path_str} --diff"); - - let output = Command::new(binary_path) - .args(["--lint", file_path_str, "--fix", file_path_str, "--diff"]) - .output() - .expect("Failed to execute command"); - - // Check that the command succeeded - assert!(output.status.success(), "Command failed: {output:?}"); - - // Convert output to string - let output_str = String::from_utf8_lossy(&output.stdout); - - // Check that the diff contains the expected replacement - assert!( - output_str.contains("-store myVariable as 42"), - "Diff doesn't contain the original line: {output_str}" - ); - assert!( - output_str.contains("+store my_variable as 42"), - "Diff doesn't contain the fixed line: {output_str}" - ); -} diff --git a/tests/control_flow.rs b/tests/control_flow.rs deleted file mode 100644 index 180a086e..00000000 --- a/tests/control_flow.rs +++ /dev/null @@ -1,250 +0,0 @@ -use wfl::interpreter::Interpreter; -use wfl::interpreter::value::Value; -use wfl::lexer::lex_wfl_with_positions; -use wfl::parser::Parser; - -async fn execute_wfl(code: &str) -> Result { - let tokens = lex_wfl_with_positions(code); - let mut parser = Parser::new(&tokens); - let program = parser.parse().map_err(|e| format!("Parse error: {e:?}"))?; - - let mut interpreter = Interpreter::default(); - interpreter - .interpret(&program) - .await - .map_err(|e| format!("Runtime error: {e:?}")) -} - -#[tokio::test] -async fn test_break_in_forever_loop() { - let code = r#" - store counter as 0 - repeat forever: - change counter to counter plus 1 - check if counter is greater than 5: - break - end check - end repeat - display counter - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); // The last statement is display, which returns Null -} - -#[tokio::test] -async fn test_break_in_count_loop() { - let code = r#" - store result as 0 - count from 1 to 10: - change result to result plus count - check if count is greater than 5: - break - end check - end count - display result - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_break_in_repeat_while_loop() { - let code = r#" - store counter as 0 - store result as 0 - repeat while counter is less than 10: - change counter to counter plus 1 - change result to result plus counter - check if counter is greater than 5: - break - end check - end repeat - display result - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_break_in_repeat_until_loop() { - let code = r#" - store counter as 0 - store result as 0 - repeat until counter is greater than 9: - change counter to counter plus 1 - change result to result plus counter - check if counter is greater than 5: - break - end check - end repeat - display result - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_continue_in_count_loop() { - let code = r#" - store result as 0 - count from 1 to 10: - check if count is equal to 2: - continue - end check - change result to result plus count - end count - display result - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_exit_from_nested_loops() { - let code = r#" - store result as 0 - count from 1 to 5: - count from 1 to 5: - change result to result plus 1 - check if count is equal to 3: - exit loop - end check - end count - end count - display result - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_nested_loops_with_break() { - let code = r#" - store outer_count as 0 - store inner_count as 0 - store total as 0 - - repeat while outer_count is less than 5: - change outer_count to outer_count plus 1 - store inner_count as 0 - - repeat while inner_count is less than 5: - change inner_count to inner_count plus 1 - change total to total plus 1 - - check if inner_count is equal to 3: - break - end check - end repeat - end repeat - - display total - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_return_from_action() { - let code = r#" - define action called test_return needs x: - check if x is greater than 5: - give back x - end check - give back x times 2 - end action - - store result as test_return with 10 - display result - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_return_from_loop_in_action() { - let code = r#" - define action called find_value needs target: - count from 1 to 10: - check if count is equal to target: - give back count - end check - end count - give back 0 - end action - - store result as find_value with 5 - display result - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_break_from_foreach_loop() { - let code = r#" - store items as [] - push with items and 1 - push with items and 2 - push with items and 3 - push with items and 4 - push with items and 5 - push with items and 6 - push with items and 7 - push with items and 8 - push with items and 9 - push with items and 10 - store sum as 0 - - for each item in items: - change sum to sum plus item - check if item is greater than 4: - break - end check - end for - - display sum - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} - -#[tokio::test] -async fn test_continue_from_foreach_loop() { - let code = r#" - store items as [] - push with items and 1 - push with items and 2 - push with items and 3 - push with items and 4 - push with items and 5 - push with items and 6 - push with items and 7 - push with items and 8 - push with items and 9 - push with items and 10 - store sum as 0 - - for each item in items: - check if (item divided by 2) times 2 is equal to item: - continue - end check - change sum to sum plus item - end for - - display sum - "#; - - let result = execute_wfl(code).await.unwrap(); - assert_eq!(result, Value::Null); -} diff --git a/tests/fixtures/broken_project/.wflcfg b/tests/fixtures/broken_project/.wflcfg deleted file mode 100644 index 161fe291..00000000 --- a/tests/fixtures/broken_project/.wflcfg +++ /dev/null @@ -1,6 +0,0 @@ -# Broken WFL configuration -timeout_seconds = potato -unknown_setting = value -log_level = extreme -max_line_length = -10 -trailing_whitespace = maybe diff --git a/tests/fixtures/nexus.wfl b/tests/fixtures/nexus.wfl deleted file mode 100644 index 4e3091a2..00000000 --- a/tests/fixtures/nexus.wfl +++ /dev/null @@ -1,8 +0,0 @@ -define action called log_message needs message_text: - open file at "test.log" as log_file - wait for write content message_text into log_file - wait for append content "\n" into log_file - close file log_file -end action - -log_message with "Starting Nexus WFL Integration Test Suite..." diff --git a/tests/fixtures/valid_project/.wflcfg b/tests/fixtures/valid_project/.wflcfg deleted file mode 100644 index 4c13e2f2..00000000 --- a/tests/fixtures/valid_project/.wflcfg +++ /dev/null @@ -1,10 +0,0 @@ -# Valid WFL configuration -timeout_seconds = 30 -logging_enabled = true -log_level = info -max_line_length = 80 -max_nesting_depth = 4 -indent_size = 2 -snake_case_variables = true -trailing_whitespace = false -consistent_keyword_case = true diff --git a/tests/integration/cli_tests.rs b/tests/integration/cli_tests.rs deleted file mode 100644 index 8f8925bb..00000000 --- a/tests/integration/cli_tests.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::fs; -use std::io::Write; -use std::path::Path; -use std::process::Command; -use tempfile::tempdir; - -#[test] -fn test_lint_fix_diff_combined() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let file_path = temp_dir.path().join("test_file.wfl"); - - let test_content = r#" -store myVariable as 42 -display myVariable -"#; - - fs::write(&file_path, test_content).expect("Failed to write test file"); - - let output = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(&["--lint", "--fix", "--diff", file_path.to_str().unwrap()]) - .output() - .expect("Failed to execute command"); - - assert!(output.status.success(), "Command failed: {:?}", output); - - let output_str = String::from_utf8_lossy(&output.stdout); - - assert!(output_str.contains("-store myVariable as 42"), - "Diff doesn't contain the original line: {}", output_str); - assert!(output_str.contains("+store my_variable as 42"), - "Diff doesn't contain the fixed line: {}", output_str); -} diff --git a/tests/integration/config_cli_tests.rs b/tests/integration/config_cli_tests.rs deleted file mode 100644 index d4dbc17c..00000000 --- a/tests/integration/config_cli_tests.rs +++ /dev/null @@ -1,125 +0,0 @@ -use std::fs; -use std::path::Path; -use std::process::Command; -use tempfile::tempdir; - -#[test] -fn test_config_check_valid() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let config_path = temp_dir.path().join(".wflcfg"); - - let config_content = r#" -# Valid configuration -timeout_seconds = 30 -logging_enabled = true -log_level = info -max_line_length = 80 -"#; - - fs::write(&config_path, config_content).expect("Failed to write config file"); - - let output = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(&["--configCheck", temp_dir.path().to_str().unwrap()]) - .output() - .expect("Failed to execute command"); - - assert!(output.status.success(), "Command failed: {:?}", output); - - let output_str = String::from_utf8_lossy(&output.stdout); - assert!(output_str.contains("✅ No configuration issues found!"), - "Output doesn't contain success message: {}", output_str); -} - -#[test] -fn test_config_check_invalid() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let config_path = temp_dir.path().join(".wflcfg"); - - let config_content = r#" -# Invalid configuration -timeout_seconds = potato -unknown_key = value -"#; - - fs::write(&config_path, config_content).expect("Failed to write config file"); - - let output = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(&["--configCheck", temp_dir.path().to_str().unwrap()]) - .output() - .expect("Failed to execute command"); - - assert!(!output.status.success(), "Command should have failed"); - assert_eq!(output.status.code(), Some(1), "Expected exit code 1"); - - let output_str = String::from_utf8_lossy(&output.stdout); - assert!(output_str.contains("❌ Error: Invalid type for timeout_seconds"), - "Output doesn't contain error message: {}", output_str); - assert!(output_str.contains("⚠️ Warning: Unknown configuration key"), - "Output doesn't contain warning message: {}", output_str); -} - -#[test] -fn test_config_fix() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let config_path = temp_dir.path().join(".wflcfg"); - - let config_content = r#" -# Invalid configuration -timeout_seconds = potato -"#; - - fs::write(&config_path, config_content).expect("Failed to write config file"); - - let output = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(&["--configFix", temp_dir.path().to_str().unwrap()]) - .output() - .expect("Failed to execute command"); - - assert!(output.status.success(), "Command failed: {:?}", output); - - let output_str = String::from_utf8_lossy(&output.stdout); - assert!(output_str.contains("✅ Fixed value for 'timeout_seconds'"), - "Output doesn't contain fix message: {}", output_str); - - let fixed_content = fs::read_to_string(&config_path).expect("Failed to read fixed config"); - assert!(fixed_content.contains("timeout_seconds = 60"), - "Config file wasn't fixed correctly: {}", fixed_content); - - let check_output = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(&["--configCheck", temp_dir.path().to_str().unwrap()]) - .output() - .expect("Failed to execute command"); - - assert!(check_output.status.success(), "Check command failed after fix: {:?}", check_output); - - let check_output_str = String::from_utf8_lossy(&check_output.stdout); - assert!(check_output_str.contains("✅ No configuration issues found!"), - "Output doesn't contain success message after fix: {}", check_output_str); -} - -#[test] -fn test_config_check_no_args() { - let output = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(&["--configCheck"]) - .output() - .expect("Failed to execute command"); - - let output_str = String::from_utf8_lossy(&output.stdout); - assert!(output_str.contains("Checking WFL configuration"), - "Output doesn't contain expected message: {}", output_str); -} - -#[test] -fn test_config_flags_mutually_exclusive() { - let output = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(&["--configCheck", "--configFix"]) - .output() - .expect("Failed to execute command"); - - assert!(!output.status.success(), "Command should have failed"); - assert_eq!(output.status.code(), Some(2), "Expected exit code 2"); - - let error_str = String::from_utf8_lossy(&output.stderr); - assert!(error_str.contains("cannot be combined with"), - "Error doesn't contain expected message: {}", error_str); -} diff --git a/tests/integration/nexus.rs b/tests/integration/nexus.rs deleted file mode 100644 index 52900c17..00000000 --- a/tests/integration/nexus.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::fs; -use std::path::Path; -use wfl::interpreter::Interpreter; -use wfl::lexer::lex_wfl_with_positions; -use wfl::parser::Parser; - -#[tokio::test] -async fn test_nexus_fixture_runs() { - // Path to the test fixture - let fixture_path = "tests/fixtures/nexus.wfl"; - let log_path = "test.log"; - - // Remove log file if it exists - let _ = fs::remove_file(log_path); - - // Read the fixture file - let source = fs::read_to_string(fixture_path).expect("Failed to read fixture file"); - - // Parse and interpret - let tokens = lex_wfl_with_positions(&source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().expect("Failed to parse program"); - - // Run the interpreter - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - assert!(result.is_ok(), "Failed to execute program: {:?}", result); - - std::thread::sleep(std::time::Duration::from_millis(100)); - - // Verify log file was created and contains expected content - assert!(Path::new(log_path).exists(), "Log file was not created"); - - let log_content = fs::read_to_string(log_path).expect("Failed to read log file"); - assert!( - log_content.contains("Starting Nexus WFL Integration Test Suite..."), - "Log file does not contain expected content" - ); -} diff --git a/tests/interpreter/container_tests.rs b/tests/interpreter/container_tests.rs deleted file mode 100644 index dc905a92..00000000 --- a/tests/interpreter/container_tests.rs +++ /dev/null @@ -1,231 +0,0 @@ -use wfl::interpreter::Interpreter; -use wfl::parser::Parser; -use wfl::lexer::Lexer; -use wfl::interpreter::value::Value; -use tokio; - -#[tokio::test] -async fn test_container_instantiation() { - let input = r#" -create container Person: - property name: Text - property age: Number -end - -create new Person as alice: - name = "Alice" - age = 28 -"#; - - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - - assert!(result.is_ok(), "Container instantiation should succeed"); -} - -#[tokio::test] -async fn test_container_method_call() { - let input = r#" -create container Person: - property name: Text - property age: Number - - action greet: - display "Hello, I am " + this.name + " and I am " + this.age + "." - end -end - -create new Person as alice: - name = "Alice" - age = 28 - -alice.greet() -"#; - - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - - assert!(result.is_ok(), "Container method call should succeed"); -} - -#[tokio::test] -async fn test_container_property_access() { - let input = r#" -create container Person: - property name: Text - property age: Number -end - -create new Person as alice: - name = "Alice" - age = 28 - -display alice.name -display alice.age -"#; - - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - - assert!(result.is_ok(), "Container property access should succeed"); -} - -#[tokio::test] -async fn test_container_inheritance() { - let input = r#" -create container Animal: - property species: Text - - action speak: - display "Animal sound" - end -end - -create container Dog extends Animal: - property breed: Text - - action speak: - display "Woof!" - end -end - -create new Dog as buddy: - species = "Canine" - breed = "Golden Retriever" - -buddy.speak() -display buddy.species -"#; - - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - - assert!(result.is_ok(), "Container inheritance should work"); -} - -#[tokio::test] -async fn test_undefined_method_call_failure() { - let input = r#" -create container Person: - property name: Text -end - -create new Person as alice: - name = "Alice" - -alice.undefined_method() -"#; - - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - - assert!(result.is_err(), "Calling undefined method should fail"); -} - -#[tokio::test] -async fn test_undefined_property_access_failure() { - let input = r#" -create container Person: - property name: Text -end - -create new Person as alice: - name = "Alice" - -display alice.undefined_property -"#; - - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - - assert!(result.is_err(), "Accessing undefined property should fail"); -} - -#[tokio::test] -async fn test_static_member_access() { - let input = r#" -create container Math: - static property PI: Number = 3.14159 - - static action square needs value: Number: Number - return value * value - end -end - -display Math.PI -store Math.square(5) in result -display result -"#; - - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - - assert!(result.is_ok(), "Static member access should work"); -} - -#[tokio::test] -async fn test_interface_implementation() { - let input = r#" -create interface Drawable: - action draw: -end - -create container Circle implements Drawable: - property radius: Number - - action draw: - display "Drawing a circle with radius " + this.radius - end -end - -create new Circle as c: - radius = 5 - -c.draw() -"#; - - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - - assert!(result.is_ok(), "Interface implementation should work"); -} diff --git a/tests/log_message_memory.rs b/tests/log_message_memory.rs deleted file mode 100644 index 47d079df..00000000 --- a/tests/log_message_memory.rs +++ /dev/null @@ -1,70 +0,0 @@ -#[cfg(feature = "dhat-heap")] -mod tests { - use std::fs; - use std::rc::Rc; - use wfl::interpreter::Interpreter; - use wfl::lexer::lex_wfl_with_positions; - use wfl::parser::Parser; - - #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_log_message_memory_usage() { - // Initialize the heap profiler for this test - let _profiler = dhat::Profiler::builder().testing().build(); - - let log_path = "temp_nexus.log"; - let _ = fs::remove_file(log_path); // Remove if exists - - // Create a minimal test case that creates a function with weak environment reference - // This directly tests the fix for the closure reference cycle - let source = r#" - define action called log_message needs message_text: - // Simple action definition to test memory leak fix - display message_text - end action - - // Call the action to make sure it works - log_message with "This is a test message" - "#; - - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().unwrap(); - - // Short timeout to ensure test completes quickly - let mut interpreter = Interpreter::with_timeout(2); - let result = interpreter.interpret(&program).await; - assert!(result.is_ok(), "Error executing script: {result:?}"); - - // Get memory stats after execution - let stats = dhat::HeapStats::get(); - println!("Max memory usage: {} bytes", stats.max_bytes); - println!("Total allocations: {}", stats.total_blocks); - - // Check that memory usage is reasonable - // With the fixed Weak> references, memory should be much lower - assert!( - stats.max_bytes < 1024 * 1024, // Much lower limit than previous memory usage - "Max memory usage was too high: {} bytes >= 1 MB", - stats.max_bytes - ); - - // Also check the total number of allocations is reasonable - assert!( - stats.total_blocks < 20000, - "Total allocations too high: {} >= 20000", - stats.total_blocks - ); - - // Most importantly, verify we fixed the reference cycle - // Check that the global environment has only one reference after execution - // This means no lingering reference cycles involving function closures - let global_env = interpreter.global_env(); - let rc_count = Rc::strong_count(global_env); - assert_eq!( - rc_count, 1, - "Global environment should have exactly one reference, but had {rc_count}" - ); - - drop(interpreter); // Explicitly drop to ensure cleanup - } -} diff --git a/tests/memory_usage.rs b/tests/memory_usage.rs deleted file mode 100644 index cc691246..00000000 --- a/tests/memory_usage.rs +++ /dev/null @@ -1,143 +0,0 @@ -#[cfg(feature = "dhat-heap")] -mod tests { - - use std::rc::Rc; - use wfl::interpreter::Interpreter; - use wfl::lexer::lex_wfl_with_positions; - use wfl::parser::Parser; - - #[test] - fn basic_allocations() { - let _profiler = dhat::Profiler::builder().testing().build(); - - let v = vec![1u8; 256]; - - let stats = dhat::HeapStats::get(); - - assert!( - stats.max_bytes < 1024, - "Max bytes exceeded limit: {} >= 1024", - stats.max_bytes - ); - drop(v); - } - - #[tokio::test] - async fn interpreter_small_program() { - let _profiler = dhat::Profiler::builder().testing().build(); - - let source = r#" - store x as 42 - store y as x plus 10 - "#; - - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().unwrap(); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - assert!(result.is_ok()); - - let stats = dhat::HeapStats::get(); - - assert!( - stats.max_bytes < 15 * 1024, // Increased limit to account for step mode overhead - "Max bytes exceeded limit: {} >= {}", - stats.max_bytes, - 15 * 1024 - ); - - drop(interpreter); - } - - #[tokio::test] - async fn test_functions_memory_usage() { - let _profiler = dhat::Profiler::builder().testing().build(); - - let source = r#" - define action called double needs x: - return x times 2 - end action - - store result as double with 21 - "#; - - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().unwrap(); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - assert!(result.is_ok()); - - let stats = dhat::HeapStats::get(); - - assert!( - stats.max_bytes < 20 * 1024, - "Max bytes exceeded limit: {} >= {}", - stats.max_bytes, - 20 * 1024 - ); - assert!( - stats.total_blocks < 1000, - "Total blocks exceeded limit: {} >= 1000", - stats.total_blocks - ); - - drop(interpreter); - } - - #[tokio::test] - async fn test_environment_memory_usage() { - let _profiler = dhat::Profiler::builder().testing().build(); - - let source = r#" - store global_var as "global" - - define action called create_counter: - store counter_value as 0 - - define action called increment: - store counter_value as counter_value plus 1 - return counter_value - end action - - return increment - end action - - store counter as create_counter with nothing - store result1 as counter with nothing - store result2 as counter with nothing - "#; - - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().unwrap(); - - let mut interpreter = Interpreter::new(); - let result = interpreter.interpret(&program).await; - assert!(result.is_ok()); - - let global_env = interpreter.global_env(); - let result2 = global_env.borrow().get("result2").unwrap(); - assert_eq!(result2.to_string(), "2"); - - let stats = dhat::HeapStats::get(); - - assert!( - stats.max_bytes < 25 * 1024, - "Max bytes exceeded limit: {} >= {}", - stats.max_bytes, - 25 * 1024 - ); - - let rc_count = Rc::strong_count(global_env); - assert_eq!( - rc_count, 1, - "Global environment should have exactly one reference" - ); - - drop(interpreter); - } -} diff --git a/tests/parser/container_err.rs b/tests/parser/container_err.rs deleted file mode 100644 index 4a551f0e..00000000 --- a/tests/parser/container_err.rs +++ /dev/null @@ -1,103 +0,0 @@ -use wfl::parser::Parser; -use wfl::lexer::Lexer; - -#[test] -fn test_missing_colon_after_container_name() { - let input = r#" -create container Person -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let result = parser.parse(); - - assert!(result.is_err(), "Expected parse error for missing colon"); -} - -#[test] -fn test_missing_end_keyword() { - let input = r#" -create container Person: - property name: Text -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let result = parser.parse(); - - assert!(result.is_err(), "Expected parse error for missing end keyword"); -} - -#[test] -fn test_invalid_property_syntax() { - let input = r#" -create container Person: - property name -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let result = parser.parse(); - - assert!(result.is_err(), "Expected parse error for invalid property syntax"); -} - -#[test] -fn test_undefined_parent_class() { - let input = r#" -create container Dog extends UndefinedAnimal: -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let result = parser.parse(); - - assert!(result.is_ok(), "Parser should succeed, typechecker should catch error"); -} - -#[test] -fn test_invalid_method_syntax() { - let input = r#" -create container Person: - action greet - display "Hello" - end -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let result = parser.parse(); - - assert!(result.is_err(), "Expected parse error for missing colon after action"); -} - -#[test] -fn test_invalid_instantiation_syntax() { - let input = r#" -create new Person alice: -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let result = parser.parse(); - - assert!(result.is_err(), "Expected parse error for missing 'as' keyword"); -} - -#[test] -fn test_empty_container_body() { - let input = r#" -create container Person: -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let result = parser.parse(); - - assert!(result.is_ok(), "Empty container body should be valid"); -} diff --git a/tests/parser/container_ok.rs b/tests/parser/container_ok.rs deleted file mode 100644 index da2d6d31..00000000 --- a/tests/parser/container_ok.rs +++ /dev/null @@ -1,117 +0,0 @@ -use wfl::parser::Parser; -use wfl::lexer::Lexer; - -#[test] -fn test_basic_container_definition() { - let input = r#" -create container Person: -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 1); -} - -#[test] -fn test_container_with_properties() { - let input = r#" -create container Person: - property name: Text - property age: Number -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 1); -} - -#[test] -fn test_container_with_methods() { - let input = r#" -create container Person: - action greet: - display "Hello" - end -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 1); -} - -#[test] -fn test_container_instantiation() { - let input = r#" -create container Person: -end - -create new Person as alice: -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 2); -} - -#[test] -fn test_container_with_inheritance() { - let input = r#" -create container Animal: -end - -create container Dog extends Animal: -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 2); -} - -#[test] -fn test_container_with_interface_implementation() { - let input = r#" -create interface Drawable: -end - -create container Shape implements Drawable: -end -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 2); -} - -#[test] -fn test_container_with_property_initializers() { - let input = r#" -create container Person: - property name: Text -end - -create new Person as alice: - name = "Alice" -"#; - let mut lexer = Lexer::new(input); - let tokens = lexer.tokenize().expect("Failed to tokenize"); - let mut parser = Parser::new(tokens); - let program = parser.parse().expect("Failed to parse program"); - - assert_eq!(program.statements.len(), 2); -} diff --git a/tests/parser_write_modes.rs b/tests/parser_write_modes.rs deleted file mode 100644 index 9c21fa09..00000000 --- a/tests/parser_write_modes.rs +++ /dev/null @@ -1,45 +0,0 @@ -use wfl::lexer::lex_wfl_with_positions; -use wfl::parser::{ - Parser, - ast::{Statement, WriteMode}, -}; - -#[test] -fn test_parse_write_statement() { - let source = r#"wait for write content "test" into logHandle"#; - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().unwrap(); - - assert_eq!(program.statements.len(), 1); - - if let Statement::WaitForStatement { inner, .. } = &program.statements[0] { - if let Statement::WriteFileStatement { mode, .. } = inner.as_ref() { - assert!(matches!(mode, WriteMode::Overwrite)); - } else { - panic!("Expected WriteFileStatement"); - } - } else { - panic!("Expected WaitForStatement"); - } -} - -#[test] -fn test_parse_append_statement() { - let source = r#"wait for append content "test" into logHandle"#; - let tokens = lex_wfl_with_positions(source); - let mut parser = Parser::new(&tokens); - let program = parser.parse().unwrap(); - - assert_eq!(program.statements.len(), 1); - - if let Statement::WaitForStatement { inner, .. } = &program.statements[0] { - if let Statement::WriteFileStatement { mode, .. } = inner.as_ref() { - assert!(matches!(mode, WriteMode::Append)); - } else { - panic!("Expected WriteFileStatement"); - } - } else { - panic!("Expected WaitForStatement"); - } -} diff --git a/tests/step_mode.rs b/tests/step_mode.rs deleted file mode 100644 index 39c88ec6..00000000 --- a/tests/step_mode.rs +++ /dev/null @@ -1,211 +0,0 @@ -use std::fs; -use std::io::Write; -use std::process::{Command, Stdio}; -use tempfile::tempdir; - -#[test] -fn test_no_step_flag_regression() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let file_path = temp_dir.path().join("test_script.wfl"); - - let test_content = r#" -store x as 42 -display x -"#; - fs::write(&file_path, test_content).expect("Failed to write test file"); - - let output_no_step = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args([file_path.to_str().unwrap()]) - .output() - .expect("Failed to execute command"); - - assert!( - output_no_step.status.success(), - "Command failed without --step flag" - ); - - let output_str = String::from_utf8_lossy(&output_no_step.stdout); - assert!( - !output_str.contains("continue (y/n)?"), - "Output shouldn't contain step mode prompts: {output_str}" - ); -} - -#[test] -fn test_step_flag_with_input() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let file_path = temp_dir.path().join("test_script.wfl"); - - let test_content = r#" -store x as 42 -display x -store y as 100 -"#; - fs::write(&file_path, test_content).expect("Failed to write test file"); - - let mut child = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(["--step", file_path.to_str().unwrap()]) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("Failed to spawn command"); - - { - let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - stdin - .write_all(b"y\ny\ny\ny\ny\n") - .expect("Failed to write to stdin"); - } - - let output = child - .wait_with_output() - .expect("Failed to wait for command"); - - let output_str = String::from_utf8_lossy(&output.stdout); - assert!( - output_str.contains("continue (y/n)?"), - "Output should contain step mode prompts: {output_str}" - ); - - let prompt_count = output_str.matches("continue (y/n)?").count(); - assert!( - prompt_count >= 1, - "Expected at least 1 prompt, got {prompt_count}" - ); -} - -#[test] -fn test_function_call_stack() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let file_path = temp_dir.path().join("test_script.wfl"); - - let test_content = r#" -define action called main: - store x as 10 - helper_function with x -end action - -define action called helper_function needs v: - display "In helper with value: " with v - nested_function -end action - -define action called nested_function: - display "In nested function" -end action - -main -"#; - fs::write(&file_path, test_content).expect("Failed to write test file"); - - let mut child = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(["--step", file_path.to_str().unwrap()]) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("Failed to spawn command"); - - { - let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - for _ in 0..20 { - stdin.write_all(b"y\n").expect("Failed to write to stdin"); - } - } - - let output = child - .wait_with_output() - .expect("Failed to wait for command"); - - let output_str = String::from_utf8_lossy(&output.stdout); - assert!( - output_str.contains("Boot phase: Configuration loaded"), - "Output should show boot phase: {output_str}" - ); - assert!( - output_str.contains("continue (y/n)?"), - "Output should contain prompts: {output_str}" - ); - assert!( - output_str.contains("Program has 4 statements"), - "Output should show program statement count: {output_str}" - ); -} - -#[test] -fn test_loop_iteration() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let file_path = temp_dir.path().join("test_script.wfl"); - - let test_content = r#" -count from 1 to 3: - store loopcounter as count - display "Count: " with loopcounter -end count -"#; - fs::write(&file_path, test_content).expect("Failed to write test file"); - - let mut child = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(["--step", file_path.to_str().unwrap()]) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("Failed to spawn command"); - - { - let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - for _ in 0..15 { - stdin.write_all(b"y\n").expect("Failed to write to stdin"); - } - } - - let output = child - .wait_with_output() - .expect("Failed to wait for command"); - - let output_str = String::from_utf8_lossy(&output.stdout); - assert!( - output_str.contains("loopcounter"), - "Output should show loopcounter variable: {output_str}" - ); - assert!( - output_str.contains("Count: 1"), - "Output should show Count: 1: {output_str}" - ); -} - -#[test] -fn test_invalid_input_handling() { - let temp_dir = tempdir().expect("Failed to create temp directory"); - let file_path = temp_dir.path().join("test_script.wfl"); - - let test_content = r#" -store x as 42 -display x -"#; - fs::write(&file_path, test_content).expect("Failed to write test file"); - - let mut child = Command::new(env!("CARGO_BIN_EXE_wfl")) - .args(["--step", file_path.to_str().unwrap()]) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("Failed to spawn command"); - - { - let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - stdin - .write_all(b"foo\nbar\ny\nn\n") - .expect("Failed to write to stdin"); - } - - let output = child - .wait_with_output() - .expect("Failed to wait for command"); - - let output_str = String::from_utf8_lossy(&output.stdout); - let prompt_count = output_str.matches("continue (y/n)?").count(); - assert!( - prompt_count >= 1, - "Expected at least one prompt, got {prompt_count}" - ); -} diff --git a/webserver.md b/webserver.md deleted file mode 100644 index 46fe8aaf..00000000 --- a/webserver.md +++ /dev/null @@ -1,472 +0,0 @@ -# Web Server Implementation Plan for WFL - -## Executive Summary - -This document outlines a comprehensive plan to implement web server capabilities in WFL (WebFirst Language). The goal is to enable WFL developers to create HTTP servers using natural language syntax consistent with WFL's design philosophy. - -## Current State Analysis - -### Existing Capabilities -Based on analysis of the WFL codebase, the following relevant features already exist: - -1. **HTTP Client Support** - - `open url at "https://..." and read content as response` syntax for GET requests - - Async/await support through Tokio runtime (v1.35.1) - - Reqwest library (v0.11.24) for HTTP client operations - -2. **Async Infrastructure** - - Full Tokio async runtime integration - - `wait for` (await) keyword for async operations - - Async action definitions - -3. **Main Loop Feature** - - `main loop:` construct for long-running processes - - Break conditions for controlled termination - - Perfect foundation for server event loops - -4. **File I/O** - - Read/write file operations - - File streaming capabilities - - Path manipulation - -5. **Pattern Matching** - - Regex-based pattern matching - - Text parsing capabilities - - Useful for parsing HTTP headers and URLs - -### Missing Components -The following components need to be implemented for web server functionality: - -1. **TCP Server Primitives** - - Socket binding and listening - - Accept incoming connections - - Connection management - -2. **HTTP Protocol Handling** - - Request parsing (method, path, headers, body) - - Response generation (status codes, headers, body) - - HTTP/1.1 protocol compliance - -3. **Request Routing** - - Path-based routing - - Method-based routing (GET, POST, etc.) - - Parameter extraction - -4. **Middleware System** - - Request/response interceptors - - Authentication/authorization - - Logging and monitoring - -5. **Advanced Features** - - WebSocket support - - Server-sent events (SSE) - - Static file serving - - Request body parsing (JSON, form data) - -## Implementation Phases - -### Phase 1: TCP Server Primitives (Foundation) - -#### 1.1 New Standard Library Module: `network` -Create `src/stdlib/network.rs` with TCP server capabilities: - -```rust -// Key functions to implement: -- listen_on_port(port: u16) -> TcpListener -- accept_connection(listener) -> TcpStream -- read_from_connection(stream) -> String -- write_to_connection(stream, data) -- close_connection(stream) -``` - -#### 1.2 WFL Syntax Extensions -Add natural language constructs for TCP operations: - -```wfl -// Listen on a port -listen on port 8080 as server - -// Accept connections -wait for connection on server as client - -// Read/write to connections -read request from client as request_data -write response to client -``` - -#### 1.3 Parser Updates -- Add tokens: `KeywordListen`, `KeywordPort`, `KeywordConnection` -- Add AST nodes: `Expression::Listen`, `Statement::AcceptConnection` -- Update interpreter to handle new operations - -### Phase 2: HTTP Request/Response Handling - -#### 2.1 HTTP Parser Implementation -Create HTTP request parser that extracts: -- Method (GET, POST, PUT, DELETE, etc.) -- Path and query parameters -- Headers -- Body - -#### 2.2 HTTP Response Builder -Implement response construction: -- Status codes (200, 404, 500, etc.) -- Headers (Content-Type, Content-Length, etc.) -- Body encoding - -#### 2.3 WFL HTTP Abstractions -Natural language syntax for HTTP concepts: - -```wfl -// Parse HTTP request -parse http request from request_data as request - -// Access request properties -store method as request's method -store path as request's path -store headers as request's headers -store body as request's body - -// Build HTTP response -create http response with status 200 as response -set response's header "Content-Type" to "text/html" -set response's body to "

Hello World

" -``` - -### Phase 3: Request Routing and Handlers - -#### 3.1 Route Definition Syntax -Implement routing with natural language: - -```wfl -// Define routes -when request matches "GET /" then: - perform handle_home with request -end when - -when request matches "POST /api/users" then: - perform handle_create_user with request -end when - -when request matches pattern "GET /users/{id}" then: - store user_id as extract "id" from request - perform handle_get_user with user_id -end when -``` - -#### 3.2 Route Matching Engine -- Path pattern matching -- Parameter extraction -- Method filtering -- 404 handling - -#### 3.3 Handler Actions -Standard handler pattern: - -```wfl -define action handle_home taking request: - create http response with status 200 as response - set response's body to read file "static/index.html" - return response -end action -``` - -### Phase 4: Complete Web Server Implementation - -#### 4.1 Server Container -Implement a reusable server container: - -```wfl -container WebServer: - property port as number - property routes as list - - action start: - listen on port self's port as server - display "Server listening on port " with self's port - - main loop: - wait for connection on server as client - wait for read request from client as request_data - - parse http request from request_data as request - store response as perform route with request - - write response to client - close client - end loop - end action - - action route taking request: - // Route matching logic - for each route in self's routes: - check if request matches route's pattern: - return perform route's handler with request - end check - end for - - // 404 response - create http response with status 404 as not_found - set not_found's body to "Page not found" - return not_found - end action -end container -``` - -#### 4.2 Usage Example -Simple web application: - -```wfl -// Create server instance -create WebServer with port 3000 as app - -// Define routes -add route "GET /" with handler home_page to app's routes -add route "GET /about" with handler about_page to app's routes -add route "POST /api/data" with handler handle_data to app's routes - -// Define handlers -define action home_page taking request: - create http response with status 200 as response - set response's header "Content-Type" to "text/html" - set response's body to "

Welcome to WFL Server

" - return response -end action - -define action about_page taking request: - create http response with status 200 as response - set response's body to "About our WFL server" - return response -end action - -define action handle_data taking request: - store data as parse json from request's body - // Process data... - create http response with status 201 as response - set response's body to "Data received" - return response -end action - -// Start server -perform app's start -``` - -### Phase 5: Advanced Features - -#### 5.1 Middleware System -Implement middleware chain: - -```wfl -// Logging middleware -define action log_requests taking request and next: - display "Request: " with request's method with " " with request's path - store response as perform next with request - display "Response: " with response's status - return response -end action - -// Authentication middleware -define action require_auth taking request and next: - check if request's header "Authorization" exists: - return perform next with request - otherwise: - create http response with status 401 as unauthorized - return unauthorized - end check -end action - -// Apply middleware -app's middleware includes log_requests -app's middleware includes require_auth for "/api/*" -``` - -#### 5.2 WebSocket Support -Enable real-time communication: - -```wfl -when request is websocket upgrade to "/ws": - accept websocket from client as socket - - repeat while socket is open: - wait for message from socket as msg - - // Echo server example - send msg to socket - end repeat -end when -``` - -#### 5.3 Static File Serving -Automatic static file handling: - -```wfl -// Serve static files from directory -serve static files from "public" at "/static" - -// With caching -serve static files from "public" at "/static" with cache for 3600 seconds -``` - -## Technical Implementation Details - -### Interpreter Extensions - -1. **New Value Types** - - `TcpListener` - Server socket - - `TcpStream` - Client connection - - `HttpRequest` - Parsed HTTP request - - `HttpResponse` - HTTP response object - -2. **Async Operations** - All network operations should be async by default: - ```rust - // In interpreter/mod.rs - Expression::Listen { port, .. } => { - let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await?; - Ok(Value::TcpListener(listener)) - } - ``` - -3. **Error Handling** - Network-specific errors: - - `PortInUse` - Port already bound - - `ConnectionFailed` - Client disconnected - - `InvalidHttpRequest` - Malformed HTTP - -### Parser Modifications - -1. **New Tokens** (in `src/lexer/mod.rs`): - ```rust - #[token("listen")] KeywordListen, - #[token("port")] KeywordPort, - #[token("connection")] KeywordConnection, - #[token("route")] KeywordRoute, - #[token("middleware")] KeywordMiddleware, - ``` - -2. **New AST Nodes** (in `src/parser/ast.rs`): - ```rust - Listen { port: Expression }, - AcceptConnection { listener: Expression }, - ParseHttpRequest { data: Expression }, - CreateHttpResponse { status: Expression }, - ``` - -### Standard Library Structure - -``` -src/stdlib/ -├── network/ -│ ├── mod.rs # Main network module -│ ├── tcp.rs # TCP primitives -│ ├── http.rs # HTTP parsing/building -│ └── websocket.rs # WebSocket support -``` - -## Testing Strategy - -### Unit Tests -- TCP connection handling -- HTTP request parsing -- Response generation -- Route matching - -### Integration Tests -Create test programs in `TestPrograms/`: -- `webserver_simple.wfl` - Basic server -- `webserver_routing.wfl` - Route testing -- `webserver_middleware.wfl` - Middleware chain -- `webserver_static.wfl` - Static file serving - -### Performance Tests -- Concurrent connection handling -- Request throughput -- Memory usage under load - -## Migration Path - -For existing WFL programs that use HTTP client features: -1. No breaking changes to existing `open url at` syntax -2. Server features are additive only -3. Gradual adoption possible - -## Example: Complete Web Application - -```wfl -// blog_server.wfl - A simple blog server - -// Database setup (using existing WFL database support) -open database at "sqlite://blog.db" as db - -// Web server setup -create WebServer with port 8080 as blog - -// Homepage route -add route "GET /" with action taking request: - store posts as perform query "SELECT * FROM posts ORDER BY created DESC LIMIT 10" on db - - store html as "

My Blog

" - - create http response with status 200 as response - set response's header "Content-Type" to "text/html" - set response's body to html - return response -end action to blog's routes - -// Individual post route -add route pattern "GET /post/{id}" with action taking request: - store post_id as extract "id" from request - store post as perform query "SELECT * FROM posts WHERE id = ?" with post_id on db - - check if post exists: - create http response with status 200 as response - set response's body to post's content - return response - otherwise: - create http response with status 404 as response - set response's body to "Post not found" - return response - end check -end action to blog's routes - -// Start the server -display "Starting blog server on http://localhost:8080" -perform blog's start -``` - -## Success Metrics - -1. **Functionality** - - Can create basic HTTP server - - Handles concurrent connections - - Supports common HTTP methods - - Routes requests correctly - -2. **Performance** - - Handle 1000+ requests/second - - Support 100+ concurrent connections - - Memory usage < 50MB for simple server - -3. **Developer Experience** - - Natural language syntax consistent with WFL - - Clear error messages - - Good documentation and examples - -## Timeline Estimate - -- **Phase 1 (TCP Primitives):** 2-3 weeks -- **Phase 2 (HTTP Handling):** 2-3 weeks -- **Phase 3 (Routing):** 1-2 weeks -- **Phase 4 (Complete Server):** 2-3 weeks -- **Phase 5 (Advanced Features):** 3-4 weeks - -**Total:** 10-15 weeks for full implementation - -## Conclusion - -Implementing web server capabilities in WFL is achievable by building on existing async infrastructure and following WFL's natural language philosophy. The phased approach allows for incremental development and testing, ensuring stability at each step. The resulting server API will be intuitive for WFL developers while providing the power needed for real web applications. \ No newline at end of file diff --git a/wfl-extension-design.md b/wfl-extension-design.md deleted file mode 100644 index ee318a7f..00000000 --- a/wfl-extension-design.md +++ /dev/null @@ -1,215 +0,0 @@ -# WFL VSCode Extension Design Document - -## Introduction - -This document outlines the design for a consolidated VSCode extension for the WebFirst Language (WFL) that provides syntax highlighting and auto-formatting capabilities. The extension is designed to operate both with and without WFL installed, providing independent functionality when WFL tools are not available. - -## Background - -Currently, there are two separate VSCode extension implementations for WFL: -1. A simpler JavaScript-based implementation in `editors/vscode-wfl` -2. A more advanced TypeScript-based implementation in `vscode-extension` - -This design aims to consolidate these implementations and enhance them with robust syntax highlighting and formatting capabilities that can operate independently of the WFL toolchain. - -## Design Goals - -1. **Extension Consolidation**: Merge the existing implementations into a single, maintainable codebase -2. **Independent Operation**: Provide core functionality without requiring WFL to be installed -3. **Enhanced Integration**: Leverage WFL tools when available for advanced functionality -4. **Comprehensive Syntax Highlighting**: Create a detailed TextMate grammar for WFL -5. **Flexible Formatting**: Support multiple formatting approaches - -## Architecture Overview - -```mermaid -graph TD - A[VSCode Extension] --> B{WFL Detection} - B -->|WFL Present| C[Enhanced Mode] - B -->|WFL Not Present| D[Independent Mode] - - C --> E[LSP Integration] - C --> F[CLI-based Formatting] - C --> G[Advanced Features] - - D --> H[Grammar-based Syntax Highlighting] - D --> I[Built-in Basic Formatter] - D --> J[Core Features Only] -``` - -## Implementation Strategy - -### 1. Extension Consolidation - -We will use the TypeScript implementation (`vscode-extension`) as our base due to its: -- Type safety and maintainability -- More structured and extensible codebase -- Comprehensive configuration options -- Proper build system - -### 2. Independent Operation - -Core features that will work without WFL installed: - -#### 2.1 Self-contained TextMate Grammar -- Comprehensive grammar not requiring semantic tokens -- Complete keyword, operator, and structure highlighting -- Proper scoping for all WFL language constructs - -#### 2.2 Built-in JavaScript-based Formatter -- Simple formatter implemented directly in the extension -- Basic indentation and alignment rules -- No dependency on external WFL tooling - -#### 2.3 Adaptive Configuration -- Detect availability of WFL tools at runtime -- Enable/disable features based on availability -- Clear user feedback on available functionality - -### 3. Enhanced Integration (When WFL is Available) - -#### 3.1 Full LSP Integration -- Diagnostics, hover information, completion -- Go-to definition, find references -- Advanced semantic highlighting - -#### 3.2 WFL CLI-based Advanced Formatting -- Integration with the WFL linter and fixer -- Full formatting capabilities -- Advanced configuration options - -## Syntax Highlighting Design - -The enhanced TextMate grammar will include: - -### Token Categories -- **Keywords**: Control flow, declarations, operators - - `define`, `action`, `store`, `display`, `if`, `check`, `otherwise`, etc. -- **Operators**: Arithmetic, comparison, logical - - `+`, `-`, `*`, `/`, `is`, `not`, `and`, `or`, etc. -- **Literals**: Strings, numbers, booleans - - String literals with proper escape handling - - Numeric literals (integers and decimals) - - Boolean literals (`yes`, `no`) -- **Functions/Actions**: Definitions and calls - - Action definitions (`define action called...`) - - Action calls - - Function parameters -- **Variables**: Declarations and references - - Variable declarations (`store ... as ...`) - - Variable references -- **Special WFL Constructs**: - - File operations (`open file`, `close file`, etc.) - - Asynchronous operations (`wait for`, etc.) - - Error handling (`try`, `when`, etc.) - -### Scoping Rules -- Block structures (`if`/`end if`, `action`/`end action`, etc.) -- Proper handling of indentation-based syntax -- Multi-line constructs - -## Auto-Formatting Design - -### Independent Formatter Features -- Indentation management (4 spaces by default) -- Alignment of related statements -- Correct spacing around operators and keywords -- Basic block structure formatting - -### WFL-Based Formatter Integration -- Integration with `wfl --lint --fix` -- Support for configuration options: - - Indent size - - Maximum line length - - Format on save - - Format on type - -## Configuration Options - -```json -"wfl.format": { - "enable": true, - "indentSize": 4, - "maxLineLength": 80, - "formatOnSave": true, - "formatOnType": false, - "provider": "auto" // "auto", "builtin", or "wfl" -}, -"wfl.lsp": { - "enable": true, - "serverPath": "wfl-lsp", - "serverArgs": [], - "versionMode": "warn" -}, -"wfl.cli": { - "path": "wfl", - "autoDetect": true -} -``` - -## Extension File Structure - -``` -vscode-wfl/ -├── package.json # Extension manifest with all configurations -├── tsconfig.json # TypeScript configuration -├── .vscodeignore # Packaging exclusion rules -├── README.md # User documentation -├── CHANGELOG.md # Version history -├── syntaxes/ -│ └── wfl.tmLanguage.json # Enhanced TextMate grammar -├── language-configuration.json # Language configuration -├── src/ -│ ├── extension.ts # Main extension entry point -│ ├── wfl-detection.ts # WFL tool detection logic -│ ├── formatting/ -│ │ ├── base-formatter.ts # Independent formatter -│ │ └── wfl-formatter.ts # WFL CLI integration -│ ├── lsp/ -│ │ └── client.ts # LSP client implementation -│ └── utils/ -│ └── config.ts # Configuration utilities -└── test/ - └── extension.test.ts # Extension tests -``` - -## Implementation Plan - -### Phase 1: Extension Consolidation -1. Set up TypeScript project structure -2. Port or merge any unique features from JavaScript version -3. Enhance TextMate grammar for basic syntax highlighting - -### Phase 2: Independent Functionality -1. Implement WFL detection logic -2. Develop basic formatter in JavaScript/TypeScript -3. Add configuration options for independent mode - -### Phase 3: Enhanced Integration -1. Enhance LSP client with graceful fallbacks -2. Implement WFL CLI formatting integration -3. Create comprehensive documentation - -## Technical Challenges - -1. **Independent Formatter Limitations**: - - The independent formatter won't match the full capabilities of WFL's native formatter - - Focus on common, predictable formatting patterns - - Clear communication of limitations to users - -2. **Grammar-only Syntax Highlighting Challenges**: - - TextMate grammar can't perform semantic analysis - - Some advanced highlighting may only work with LSP - - Need to design grammar to handle common patterns effectively - -3. **Configuration Synchronization**: - - Keeping formatting options consistent between independent and WFL-based formatters - - Providing clear UI indicators for active formatter - -4. **Graceful Feature Degradation**: - - Designing each feature to fall back gracefully - - Providing helpful messages when enhanced features are unavailable - -## Conclusion - -This design provides a pathway to a consolidated and enhanced VSCode extension for WFL that works both with and without the WFL toolchain installed. The proposed architecture allows for independent operation while leveraging WFL tools when available, ensuring a consistent and robust development experience for WFL users. \ No newline at end of file diff --git a/wfledit.md b/wfledit.md deleted file mode 100644 index 7769e5e9..00000000 --- a/wfledit.md +++ /dev/null @@ -1,281 +0,0 @@ -# WFL Editor Design Document - -## Overview - -This document outlines the design and implementation strategy for a nimble, efficient WFL code editor built in Rust for Windows. The editor will provide syntax highlighting, auto-completion, and integration with the WFL language server. - -## Technology Stack - -### GUI Framework: egui - -After extensive research, **egui** is recommended for the following reasons: - -- **Performance**: Only 30MB memory usage with minimal CPU overhead -- **Bundle Size**: Small native binary without WebView overhead -- **Text Editing**: Existing `egui_code_editor` crate with syntax highlighting -- **Development Speed**: Immediate mode GUI is simple and quick to iterate -- **Active Ecosystem**: Well-maintained with regular updates - -### Alternative: Tauri - -If richer features are needed (advanced IntelliSense, debugging UI), **Tauri** is recommended: -- Can integrate Monaco Editor (VS Code's editor) -- Web-based UI with native performance -- Larger memory footprint (50-200MB) but more features - -## Architecture - -### Core Components - -1. **Editor Core** (`wfledit-core`) - - Text buffer management - - Undo/redo system - - File I/O operations - - WFL syntax definitions - -2. **GUI Layer** (`wfledit-gui`) - - egui-based user interface - - Syntax highlighting via `egui_code_editor` - - Menu system and shortcuts - - File tree sidebar - -3. **LSP Integration** (`wfledit-lsp`) - - Communication with `wfl-lsp` - - Auto-completion - - Error diagnostics - - Go-to-definition - -4. **Configuration** (`wfledit-config`) - - User preferences - - Theme management - - Keyboard shortcuts - -## Implementation Plan - -### Phase 1: Basic Editor (Week 1-2) - -```rust -// Cargo.toml dependencies -[dependencies] -egui = "0.24" -eframe = "0.24" -egui_code_editor = "0.2" -syntect = "5.0" // For advanced syntax highlighting -rfd = "0.12" // Native file dialogs -``` - -```rust -// Basic editor structure -use egui_code_editor::{CodeEditor, ColorTheme, Syntax}; - -pub struct WflEditor { - code: String, - file_path: Option, - modified: bool, -} - -impl WflEditor { - pub fn ui(&mut self, ctx: &egui::Context) { - egui::CentralPanel::default().show(ctx, |ui| { - // Menu bar - egui::menu::bar(ui, |ui| { - ui.menu_button("File", |ui| { - if ui.button("Open...").clicked() { - self.open_file(); - } - if ui.button("Save").clicked() { - self.save_file(); - } - }); - }); - - // Code editor - CodeEditor::default() - .with_fontsize(14.0) - .with_theme(ColorTheme::GRUVBOX) - .with_syntax(self.wfl_syntax()) - .with_numlines(true) - .show(ui, &mut self.code); - }); - } -} -``` - -### Phase 2: WFL Integration (Week 3-4) - -1. **Syntax Highlighting** - ```rust - fn wfl_syntax() -> Syntax { - Syntax::new("WFL") - .with_keywords(vec![ - "store", "as", "display", "check", "if", "then", - "otherwise", "end", "count", "from", "to", "for", - "each", "in", "define", "action", "needs", "returns", - "container", "with", "property", "extending", "async", - "await", "try", "catch", "throw", "import", "export" - ]) - .with_types(vec!["text", "number", "boolean", "list", "null"]) - .with_special(vec!["true", "false", "null"]) - } - ``` - -2. **Auto-completion** - - Integration with WFL lexer tokens - - Context-aware suggestions - - Snippet support - -### Phase 3: LSP Integration (Week 5-6) - -```rust -use lsp_types::*; -use tokio::net::TcpStream; - -pub struct LspClient { - connection: TcpStream, -} - -impl LspClient { - pub async fn initialize(&mut self) -> Result { - // Initialize LSP connection - } - - pub async fn get_completions(&mut self, position: Position) -> Vec { - // Request completions from wfl-lsp - } -} -``` - -### Phase 4: Advanced Features (Week 7-8) - -1. **File Explorer** - - Tree view of project files - - Quick file switching (Ctrl+P) - - Search in files - -2. **Error Diagnostics** - - Real-time error highlighting - - Error panel with quick fixes - - Integration with WFL analyzer - -3. **Debugging Support** - - Breakpoint management - - Variable inspection - - Step-through debugging - -## UI Design - -### Layout - -``` -┌─────────────────────────────────────────────────┐ -│ File Edit View Run Help │ -├────────────┬────────────────────────────────────┤ -│ │ example.wfl │ -│ Explorer ├────────────────────────────────────┤ -│ │ 1 | store message as "Hello" │ -│ ▼ project │ 2 | display message │ -│ main.wfl │ 3 | │ -│ lib.wfl │ 4 | count from 1 to 10: │ -│ │ 5 | display index │ -│ │ 6 | end count │ -│ │ │ -├────────────┴────────────────────────────────────┤ -│ Problems (0) Output Terminal │ -└─────────────────────────────────────────────────┘ -``` - -### Keyboard Shortcuts - -- `Ctrl+S` - Save -- `Ctrl+O` - Open -- `Ctrl+N` - New file -- `Ctrl+P` - Quick open -- `Ctrl+Shift+P` - Command palette -- `Ctrl+Space` - Trigger completion -- `F12` - Go to definition -- `Shift+F12` - Find references -- `Ctrl+/` - Toggle comment -- `Alt+Up/Down` - Move line - -## Performance Targets - -- **Startup Time**: < 100ms -- **Memory Usage**: < 50MB for typical projects -- **File Open**: < 50ms for files under 1MB -- **Syntax Highlighting**: Real-time with no perceptible lag -- **Auto-completion**: < 100ms response time - -## Build Configuration - -```toml -# Cargo.toml -[package] -name = "wfledit" -version = "0.1.0" -edition = "2021" - -[profile.release] -opt-level = 3 -lto = true -codegen-units = 1 -strip = true - -[target.'cfg(windows)'.build-dependencies] -winres = "0.1" - -[package.metadata.winres] -ProductName = "WFL Editor" -FileDescription = "Lightweight code editor for WFL" -``` - -## Distribution - -1. **Standalone Binary** - - Single .exe file - - No installer required - - < 10MB size - -2. **MSI Installer** (Optional) - - Windows installer - - Start menu integration - - File association (.wfl files) - -## Testing Strategy - -1. **Unit Tests** - - Text buffer operations - - Syntax highlighting rules - - File I/O - -2. **Integration Tests** - - LSP communication - - End-to-end editing scenarios - - Performance benchmarks - -3. **Manual Testing** - - Large file handling - - Unicode support - - Accessibility features - -## Future Enhancements - -1. **Plugin System** - - Lua or Rhai scripting - - Custom themes - - Language extensions - -2. **Collaboration Features** - - Live share support - - Git integration - - Diff viewer - -3. **AI Integration** - - Code completion via AI - - Natural language to WFL - - Code explanation - -## Conclusion - -This design provides a solid foundation for a nimble, efficient WFL editor that balances simplicity with essential features. The egui-based approach ensures excellent performance and a small footprint, while the modular architecture allows for future expansion. - -The editor will serve as both a learning tool for WFL beginners and a productive environment for experienced users, maintaining the language's philosophy of accessibility and simplicity. \ No newline at end of file