Commit Graph

23 Commits

Author SHA1 Message Date
ef5b94545f fix: correct event handler registration in plugins
Fixed incorrect event handler registration in two plugins:

- Statistics plugin was calling Step instead of Stop on StopEvent,
  preventing statistics from being printed at the end of execution.
- Logs plugin was listening to StopEvent instead of StepEvent,
  causing it to only log once at the end instead of on each step.
2026-01-13 19:34:29 -05:00
307b7ffd1e refactor: replace string-based emitter with type-safe generic event system (#28)
## Description

This PR refactors the event emitter system from a string-based message passing approach to a type-safe generic implementation using typed events.
The previous system relied on string message names which were error-prone and lacked compile-time safety.
This refactoring introduces a generic `BaseEmitter[E comparable]` that provides type safety while consolidating the various tracker packages into a unified plugins architecture.

Key changes:
- Replace `Emitter` with generic `BaseEmitter[E comparable]` for type-safe event handling.
- Add `Event` type enumeration with `StartEvent`, `StepEvent`, and `StopEvent` constants.
- Create `Listener[E]` interface with `BaseListener` implementation for better abstraction.
- Consolidate `explanation`, `performance`, and `statistics` packages into unified `internal/plugins` package.
- Simplify CLI initialization by using plugin constructors that handle their own event subscriptions.
- Add `Items()` iterator method to `Set` for idiomatic Go 1.23+ range loops over sets.

### Decisions

Use generics for type-safe event handling.
This provides compile-time guarantees that event types match their handlers while maintaining flexibility for future event types.

Consolidate trackers into plugins architecture.
Previously separate packages (`explanation`, `performance`, `statistics`) now live under `internal/plugins`, making the plugin pattern explicit and easier to extend.

Plugin constructors self-register with engine.
Each plugin's `New*` constructor now handles its own event subscriptions, reducing boilerplate in the main CLI.

## Benefits

Type safety prevents runtime errors from typos in event names.
The compiler now catches mismatched event types at compile time rather than failing silently at runtime.

Cleaner plugin architecture makes adding new features easier.
New plugins follow a consistent pattern and live in a single location.

Reduced boilerplate in main CLI.
Plugin initialization is now a single function call rather than manual event registration.

Better testability through interface-based design.
The `Listener[E]` interface allows for easier mocking and testing of event handlers.

## Checklist

- [x] Code follows conventional commit format.
- [x] Branch follows naming convention (`<type>/<description>`).
- [x] Tests pass (if applicable).
- [x] Documentation updated (if applicable).

Reviewed-on: #28
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-01-14 00:30:21 +00:00
15c904ccc9 feat: improve reduction algorithm with LIFO-based iterator (#15)
## Description

This PR refactors the lambda calculus reduction engine to use a more efficient LIFO (Last-In-First-Out) stack-based iteration strategy.
Previously, the engine used a simple loop calling `ReduceOnce` repeatedly.
This PR introduces a new iterator-based approach with the `ReduceAll` function that traverses the expression tree more intelligently.

Changes include:

- Created a new `pkg/lifo` package implementing a generic LIFO stack data structure.
- Added `pkg/lambda/iterator.go` with an `Iterator` type for traversing lambda expressions.
- Refactored `pkg/lambda/reduce.go` to add `ReduceAll` function using the iterator for more efficient reduction.
- Updated `internal/engine/engine.go` to use `ReduceAll` instead of looping `ReduceOnce`.
- Renamed sample test files from `.txt` to `.test` extension.
- Fixed `.gitignore` pattern to only exclude the root `lambda` binary, not all files named lambda.
- Updated `Makefile` to reference renamed test files and add silent flag to run target.

### Decisions

- Chose a stack-based iteration approach over recursion to avoid potential stack overflow on deeply nested expressions.
- Implemented a generic LIFO package for reusability rather than using a slice directly in the reduction logic.
- Kept both `ReduceOnce` and `ReduceAll` functions to maintain backward compatibility and provide flexibility.

## Performance

Benchmark results comparing main branch vs this PR on Apple M3:

| Test | Before (ms/op) | After (ms/op) | Change |
|------|----------------|---------------|--------|
| Thunk | 0.014 | 0.014 | 0.00% |
| Fast | 1.29 | 1.20 | **-7.04%** |
| Simple | 21.51 | 6.45 | **-70.01%** |
| Church | 157.67 | 43.00 | -76.788% |
| Saccharine | 185.25 | 178.99 | **-3.38%** |

**Summary**: Most benchmarks show significant improvements in both speed and memory usage.
The Church benchmark shows a regression that needs investigation.

## Benefits

- More efficient expression tree traversal with the iterator pattern.
- Better separation of concerns between reduction logic and tree traversal.
- Generic LIFO stack can be reused in other parts of the codebase.
- Cleaner engine implementation with callback-based step emission.

## Checklist

- [x] Code follows conventional commit format.
- [x] Branch follows naming convention (`<type>/<description>`). Always use underscores.
- [ ] Tests pass (if applicable).
- [ ] Documentation updated (if applicable).

Reviewed-on: #15
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-01-12 02:16:07 +00:00
0eff85f8fa feat: add output flag (#13)
## Description

The lambda CLI previously only wrote output to stdout using shell redirection.
This PR adds support for writing results to files using the `-o` flag.
This is implemented using a new `Destination` interface that mirrors the existing `Source` pattern.

Changes:
- Added `Destination` interface with `StdoutDestination` and `FileDestination` implementations.
- Added `-o` flag to CLI argument parser for output file specification.
- Updated `Config` to use `Destination` instead of direct output handling.
- Refactored main to use `Destination.Write()` for result output.
- Updated Makefile targets (`run`, `profile`, `explain`) to use `-o` flag instead of shell redirection.

### Decisions

The `-o` flag defaults to stdout when not specified or when set to `-`.
This maintains backward compatibility while providing explicit file output capability.

## Benefits

- Cleaner command-line interface without shell redirection.
- Symmetric design with `Source` interface for input.
- More portable across different shells and environments.
- Explicit output handling improves code clarity.

## Checklist

- [x] Code follows conventional commit format.
- [x] Branch follows naming convention (`<type>/<description>`).
- [ ] Tests pass (if applicable).
- [ ] Documentation updated (if applicable).

Reviewed-on: #13
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-01-11 22:14:48 +00:00
62699a0e37 feat: add file input flag (#12)
## Description

The lambda CLI previously only supported inline string expressions and stdin input.
This PR adds support for reading lambda expressions from files using the `-f` flag.
This makes it easier to work with larger programs stored in files.

Changes:
- Added `FileSource` type to `internal/config/source.go` for reading from file paths.
- Added `-f` flag to CLI argument parser with validation to prevent conflicting inputs.
- Updated Makefile targets (`run`, `profile`, `explain`) to use `-f` flag instead of stdin redirection.

### Decisions

The `-f` flag takes precedence over positional arguments.
If both are specified, an error is returned to avoid ambiguity.

## Benefits

- More intuitive workflow for file-based lambda programs.
- Cleaner Makefile targets without stdin redirection.
- Consistent with common CLI conventions (e.g., `grep -f`, `awk -f`).

## Checklist

- [x] Code follows conventional commit format.
- [x] Branch follows naming convention (`<type>/<description>`).
- [ ] Tests pass (if applicable).
- [ ] Documentation updated (if applicable).

Reviewed-on: #12
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
2026-01-11 21:43:11 +00:00
Max
2499921679 style: moved functions around 2025-12-30 15:58:14 -05:00
Max
05cd8bc4f3 feat: meaningful comments for internal packages 2025-12-29 20:39:43 -05:00
Max
13989e4c61 style: better flag explanation 2025-12-29 20:06:35 -05:00
Max
529abb7c26 fix: clearly state fatal errors 2025-12-29 20:01:44 -05:00
Max
351faa7e08 feat: statistics flag, commented some more 2025-12-29 20:00:29 -05:00
Max
17cf8f86f8 feat: explanation as observer too 2025-12-29 01:31:09 -05:00
Max
a2ce5b6897 feat: rename profiler to performance, typeless event emitter 2025-12-29 01:15:14 -05:00
Max
c2b397a9f6 feat: observer pattern for statistics 2025-12-29 00:51:50 -05:00
Max
e9dc3fe171 feat: added optional profiling 2025-12-28 22:52:10 -05:00
Max
ee9e71d58e fix: no ds store 2025-12-28 02:07:46 -05:00
Max
0945cedf51 feat: only compute all free variables during a-conversion 2025-12-28 02:07:14 -05:00
Max
6f3b252819 style: no "this" or "self" as receiver 2025-12-26 00:00:10 -05:00
Max
e6e4a0df6f style: no punctuation or capitalization in errors 2025-12-25 23:52:39 -05:00
Max
963435c657 fix: cli.CLIOptions is repetitive 2025-12-25 23:50:17 -05:00
Max
3351eaddfc feat: better structured internal 2025-12-25 17:21:16 -05:00
Max
99703c2587 fix: unbound substitutions, explanation tag 2025-12-25 01:55:46 -05:00
Max
a56ec808ec feat: read from std in 2025-12-25 00:46:48 -05:00
Max
2c3ce9baf7 feat: wogihrsoiuvjsroirgj 2025-12-24 14:55:33 -05:00