Skip to content

Commit 07e2932

Browse files
committed
Align RPython syntax, docs, and end-to-end tests
- Make user-facing type strings match the language syntax (Int/Unit, Maybe[T], Result[Ok, Err]) - Improve ADT type parsing (allow multi-field constructors like ) - Remove stale #[ignore] tests and strengthen parser tests (avoid “is_ok()” false positives) - Add/expand Beecrowd end-to-end harness + fixtures for syntax/runtime coverage - Refresh docs: README + refactor log, and rewrite stdlib docs as STDLIB.md - Repo hygiene: ignore /llm and keep outputs consistent (lists/tuples/monads print as literals)
1 parent 5f7d200 commit 07e2932

148 files changed

Lines changed: 2630 additions & 295 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
/target
2-
.idea
2+
.idea/
3+
.ideallm/
4+
/llm/

README.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,14 @@ asserttrue(result == 120, "5! should be 120");
163163
Anonymous functions can be assigned to variables or passed as arguments.
164164

165165
```text
166-
val add = lambda (a: Int, b: Int) -> Int: return a + b end;
167-
val sum = add(2, 3);
166+
def apply(f: fn(Int, Int) -> Int, a: Int, b: Int) -> Int:
167+
return f(a, b);
168+
end;
169+
170+
val sum = apply(lambda (a: Int, b: Int) -> Int: return a + b end, 2, 3);
168171
```
169172

170-
> **Current limitation:** Lambdas are parsed correctly but not yet fully implemented in the interpreter.
173+
> **Current limitation:** Lambdas are supported as first-class function values (especially when passed as arguments to functions expecting a `fn(...) -> ...` type), but calling a lambda *via a variable name* is not fully supported yet in all contexts. Prefer `def` for named functions, or pass lambdas directly as arguments.
171174
172175
---
173176

@@ -177,13 +180,16 @@ Metabuiltins are functions implemented in Rust and exposed to user code. They ha
177180

178181
### I/O
179182

180-
| Function | Description |
181-
|---------------------------|--------------------------------------------------|
182-
| `input(prompt?)` | Read a line from stdin; optional prompt string. |
183-
| `input_int(prompt?)` | Read and parse an integer from stdin. |
184-
| `input_real(prompt?)` | Read and parse a real number from stdin. |
185-
| `print(value)` | Print `value` without trailing newline. |
186-
| `print_line(value)` | Print `value` followed by a newline. |
183+
| Function | Description |
184+
|---------------------------|----------------------------------------------------------------|
185+
| `input()` | Read a line from stdin. Returns a `String`. |
186+
| `input(prompt)` | Print `prompt`, then read a line from stdin. |
187+
| `input_int()` | Read and parse an integer from stdin. |
188+
| `input_int(prompt)` | Print `prompt`, then read and parse an integer. |
189+
| `input_real()` | Read and parse a real number from stdin. |
190+
| `input_real(prompt)` | Print `prompt`, then read and parse a real number. |
191+
| `print(value)` | Print `value` without trailing newline. |
192+
| `print_line(value)` | Print `value` followed by a newline. |
187193

188194
### Conversion
189195

@@ -202,11 +208,19 @@ Metabuiltins are functions implemented in Rust and exposed to user code. They ha
202208
| `join(values: List[String], sep)` | Join a list of strings with a separator. |
203209
| `len(value)` | Return the length of a string, list, or tuple. |
204210

211+
### Tuples
212+
213+
| Function | Description |
214+
|--------------------------|-------------|
215+
| `tuple_get(value, index)` | Return the element at `index` from a tuple (or an error string on invalid input). |
216+
205217
### Files
206218

207219
| Function | Description |
208220
|-------------------------------|-------------------------------------------------------------------|
209-
| `open(path, mode, content)` | Open a file. Modes: `r` (read), `w` (write), `a` (append). |
221+
| `open(path, "r")` | Read and return the contents of `path`. |
222+
| `open(path, "w", content)` | Write `content` to `path`, overwriting existing content. |
223+
| `open(path, "a", content)` | Append `content` to `path`. |
210224

211225
---
212226

@@ -226,7 +240,7 @@ RPython provides two monadic types for representing optional or fallible values:
226240
- `unwrap(value)` — extracts the inner value (panics if `Nothing` or `Err`)
227241
- `tryUnwrap(value)` — extracts or propagates errors automatically
228242

229-
> **Current limitation:** The parser does not yet support `Just()`, `Nothing`, `Ok()`, and `Err()` as expression syntax. These types exist in the AST and type system, and are used internally by the interpreter and type checker. Parser support for constructing these values from source code is planned for a future release.
243+
> Note: `Just(value)`, `Nothing`, `Ok(value)`, and `Err(error)` are supported as expression syntax.
230244
231245
---
232246

@@ -293,7 +307,7 @@ src/
293307
- **`type_checker/`** — Validates types; checks function signatures and return types.
294308
- **`interpreter/statement_execute.rs`** — Executes statements; handles loops with `break`/`continue`.
295309
- **`interpreter/expression_eval.rs`** — Evaluates expressions; dispatches metabuiltin calls.
296-
- **`stdlib/standard_library.rs`** — Implements 13 metabuiltins.
310+
- **`stdlib/standard_library.rs`** — Implements the metabuiltins table and its built-in functions.
297311
- **`pretty_print/`** — AST-to-source formatter (see below).
298312

299313
### Pretty Printer
@@ -376,7 +390,7 @@ The interpreter reads from stdin and writes to stdout, making it suitable for au
376390
4. **No interactive REPL:** only file-based execution is supported.
377391
5. **Limited error messages:** parser and type checker errors are functional but not always user-friendly.
378392
6. **No tail-call optimization:** deep recursion may overflow the stack.
379-
7. **Maybe/Result constructors:** `Just()`, `Nothing`, `Ok()`, `Err()` cannot be parsed from source code yet.
393+
7. **Maybe/Result constructors:** supported as expression syntax, but values cannot be destructured without pattern matching.
380394
8. **Small standard library:** only a small set of metabuiltins is available (basic I/O, conversions, simple string/list helpers); there are no rich libraries for math, dates/times, networking, etc.
381395
9. **No exceptions:** there is no `try`/`catch` mechanism or exception hierarchy; errors are represented via `Maybe`/`Result` types or abort execution with an error message.
382396
10. **No objects or methods:** there are no classes, interfaces, or method calls; programs are written with functions, lists/tuples, and algebraic data types.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# AI-Assisted Refactoring Session (March 22, 2026)
2+
3+
## Objective
4+
To align the existing R-Python language implementation (`parser`, `ast`, and `pretty_print`) precisely with the features documented in the project's `README.md`.
5+
6+
## Issues Identified
7+
During an extensive codebase scan comparing the `README.md` syntax definitions against what the R-Python Rust frontend actually implemented, several inconsistencies were detected:
8+
9+
1. **Composite Types Parsing Discrepancies**:
10+
- Lists were documented as `List[T]` but parsed natively as `[T]`.
11+
- Tuples were documented as `Tuple[T1, T2]` but parsed via parentheses as `(T1, T2)`.
12+
- Functions were documented as `fn(T1, T2) -> R` but parsed without the `fn` keyword as `(T1, T2) -> R`.
13+
14+
2. **Error Handling Implementation Gaps**:
15+
- The README listed features like `unwrap(value)`, `tryUnwrap(value)`, `isNothing(maybe)`, and `isError(result)` as working available functions.
16+
- However, they hadn't been fully mapped structurally into the primary expression parsing phase (`src/parser/parser_expr.rs`) which blocked their `.rpy` usage.
17+
18+
## Fixes Implemented
19+
20+
### 1. Abstract Syntax Tree & Parser Adjustments
21+
Modules adjusted: `src/parser/parser_type.rs` and `src/ir/ast.rs`
22+
- Adapted `parse_list_type` to use the `List` keyword wrapper for generating `Type::TList`.
23+
- Adapted `parse_tuple_type` to use the `Tuple` keyword wrapper for generating `Type::TTuple`.
24+
- Adapted `parse_function_type` to expect the `fn` keyword before parameter listings.
25+
- Formatter traits (`fmt::Display`) on AST nodes were updated to match the strict `List[...]` and `Tuple[...]` syntax, keeping compiler errors aligned with user-facing expectations.
26+
27+
### 2. Error Handling Integrated
28+
Modules adjusted: `src/parser/parser_expr.rs`
29+
- Exposed dedicated recursive parsers directly translating to native AST functional nodes:
30+
- `unwrap(expr)` routes to `Expression::Unwrap`
31+
- `tryUnwrap(expr)` routes to `Expression::Propagate`
32+
- `isNothing(expr)` routes to `Expression::IsNothing`
33+
- `isError(expr)` routes to `Expression::IsError`
34+
- Allowed true integration natively in `.rpy` files with zero runtime modifications required underneath (as interpreter and type checking logic natively recognized these AST branches already).
35+
36+
### 3. Pretty Printer Formatter Corrections
37+
Modules adjusted: `src/pretty_print/pretty_type.rs`
38+
- Reverted the `ToDoc` implementations over types to utilize `List[T]`, `Tuple[T1, T2]` and `fn(...) -> R`.
39+
- Handled indentation properly for multiline implementations passing the Rust test suite.
40+
41+
### 4. Beecrowd Edge Case Fixed
42+
- Re-tested library compilation checking legacy RPython Beecrowd tasks: discovered and fixed an invalid fixture mismatch in the tests context (`1035 - Selection Test`) which broke testing bounds. Re-aligned the IO mappings passing the entire testing suite successfully.
43+
44+
## Outcome
45+
The standard library definitions matching `README.md` constraints now genuinely behave 1:1 against file inputs while keeping functional paradigms intact. Execution and tests pass on `0.1.0`.
46+
47+
---
48+
49+
## Follow-up Refactors (March 22, 2026)
50+
51+
After the initial README-alignment work above, we performed additional refactors focused on *end-to-end feature testing* (parser → type checker → interpreter → Beecrowd fixtures) without relying on “dead code” branches.
52+
53+
### 5. Maybe/Result Constructors Supported as Expressions
54+
Modules adjusted: `src/parser/parser_expr.rs` and `README.md`
55+
- Added expression parsing for the constructors:
56+
- `Just(value)``Expression::CJust(...)`
57+
- `Nothing``Expression::CNothing`
58+
- `Ok(value)``Expression::COk(...)`
59+
- `Err(error)``Expression::CErr(...)`
60+
- Added parser unit tests covering these constructors.
61+
- Updated `README.md` to remove the outdated limitation that constructors could not be written in source code.
62+
63+
Why this mattered: the `unwrap/isNothing/isError/tryUnwrap` family becomes meaningfully testable only when source programs can actually construct `Maybe`/`Result` values.
64+
65+
### 6. Tuple Accessor Builtin (`tuple_get`)
66+
Modules adjusted: `src/stdlib/standard_library.rs` and `src/interpreter/expression_eval.rs`
67+
- Added a new metabuiltin: `tuple_get(value, index)`.
68+
- Wired it into the interpreter builtin dispatch.
69+
- Updated stdlib table tests to include the new builtin.
70+
71+
Why this mattered: without tuple element access, `Tuple[...]` can be parsed and type-checked, but is hard to use in *real* algorithmic problems (including Beecrowd), because values are otherwise opaque.
72+
73+
### 7. Beecrowd “No Workarounds” Coverage (Problem 1172)
74+
Modules/files adjusted: `tests/fixtures/beecrowd/1172/solution.rpy`
75+
- Rewrote the solution so that the newly-implemented features are exercised in the *actual* executed code path (no `if False:` blocks):
76+
- typed parameters using `List[Int]` and `Tuple[Int, Int]`
77+
- higher-order function usage via `fn(Tuple[Int, Int]) -> Int`
78+
- real runtime use of `Ok(...)` / `Err(...)` with `isError(...)` and `unwrap(...)`
79+
- tuple element retrieval via `tuple_get(...)`
80+
- Kept the same folder/fixture structure and did not change any existing Beecrowd problems already in the suite.
81+
82+
### 8. Example Script Updated (`hello_io.rpy`)
83+
File adjusted: `examples/hello_io.rpy`
84+
- Updated the demo to show the new constructors and error-handling operations without intentionally triggering a runtime panic.
85+
86+
## Updated Outcome
87+
The codebase now supports the README composite type syntax *and* can exercise `Maybe`/`Result` values end-to-end from source code, including in Beecrowd-style fixture tests, while keeping the existing tests intact.
88+
89+
### 9. Runtime Output Formatting (Lists/Tuples/Monads)
90+
Modules adjusted: `src/stdlib/standard_library.rs`
91+
- Fixed `print(...)`, `print_line(...)`, and `to_string(...)` output for composite runtime values.
92+
- Previously, tuples and lists printed using Rust `Debug` formatting (e.g., `Tuple([CInt(404), CString("Not Found")])`), leaking internal AST wrapper names.
93+
- Updated the stdlib’s internal value-to-string conversion so runtime values render using RPython literal-like formatting:
94+
- lists as `[1, 2, 3]`
95+
- tuples as `(404, "Not Found")`
96+
- monads as `Ok(999)`, `Err("boom")`, `Just(1)`, `Nothing`
97+
98+
Why this mattered: examples (like `hello_io.rpy`) and Beecrowd-style outputs should reflect *language-level* values, not internal interpreter representation.
99+
100+
### 10. Documentation Re-validation (README + STDLIB)
101+
Files adjusted: `README.md`, `src/stdlib/STDLIB.md`
102+
- Re-validated `README.md` against the current implementation.
103+
- Updated the lambda example to a pattern that is known to work reliably: passing `lambda (...) -> ...` as a first-class function argument.
104+
- Documented the `tuple_get(value, index)` builtin in the “Standard Library” section.
105+
- Removed the stale hard-coded “13 metabuiltins” count (the exact number changes as the stdlib evolves).
106+
- Renamed and rewrote the standard library documentation as `src/stdlib/STDLIB.md` and translated it to English.
107+
108+
Why this mattered: in this project, the root README acts as the user-facing language spec; keeping it and the stdlib docs synchronized avoids regressions where supported syntax exists but is undocumented (or vice-versa).
109+
110+
### 11. Type Display Formatting Alignment (User-Facing Errors)
111+
Module adjusted: `src/ir/ast.rs`
112+
- Updated `fmt::Display` for `Type` to reflect the README/parser syntax for type names:
113+
- `Int`, `Real`, `Boolean`, `String`, `Unit`, `Any`
114+
- `Maybe[T]` and `Result[Ok, Err]` (instead of angle-bracket formatting)
115+
116+
Why this mattered: type checker error messages should use the same type spellings that users write in `.rpy` programs.
117+
118+
### 12. Test Suite Hygiene (Remove `#[ignore]` + Make Assertions Meaningful)
119+
Files adjusted: `src/parser/parser_expr.rs`, `tests/parser_tests.rs`, `src/parser/parser_type.rs`
120+
- Removed `#[ignore]` from parser unit/integration tests where the underlying features are already implemented.
121+
- Updated stale expectations to match current AST semantics:
122+
- `if` parsing expectations were updated to `Statement::IfChain` (the current representation).
123+
- Adjusted the ADT-related test cases to reflect the current limitation that ADT declarations are parsed as *types* (via `parse_type`) and not as top-level statements.
124+
- Strengthened “invalid expression” tests to account for prefix-friendly parsing (a partial parse that leaves unconsumed input is treated as invalid for full-program parsing).
125+
- Extended ADT constructor type parsing to allow multiple basic field types (e.g., `| Rectangle Int Int`).
126+
127+
Why this mattered: ignored tests tend to rot and hide regressions. By making them run and ensuring they assert the right invariants, the suite becomes a better executable spec for the README.

examples/hello_io.rpy

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,45 @@ val contents2 = open(test_file, "r");
163163
var _ = print("After append: ");
164164
var _ = print_line(contents2);
165165

166+
var _ = print_line("=== 16. Explicit Typing & Error Handling ===");
167+
def print_typed_list(lst: List[Int]) -> Int:
168+
var _ = print("Typed List[Int]: ");
169+
var _ = print_line(lst);
170+
return 0;
171+
end;
172+
173+
def print_typed_tuple(tup: Tuple[Int, String]) -> Int:
174+
var _ = print("Typed Tuple[Int, String]: ");
175+
var _ = print_line(tup);
176+
return 0;
177+
end;
178+
179+
def higher_order(func: fn(Int) -> Int, value: Int) -> Int:
180+
return func(value);
181+
end;
182+
183+
def duplicate(x: Int) -> Int:
184+
return x * 2;
185+
end;
186+
187+
var _ = print_typed_list([100, 200, 300]);
188+
var _ = print_typed_tuple((404, "Not Found"));
189+
190+
var _ = print("higher_order(duplicate, 21): ");
191+
var _ = print_line(higher_order(duplicate, 21));
192+
193+
val maybe_val = Nothing;
194+
var _ = print("isNothing(Nothing)? ");
195+
var _ = print_line(isNothing(maybe_val));
196+
197+
val ok_val = Ok(999);
198+
var _ = print("isError(Ok(999))? ");
199+
var _ = print_line(isError(ok_val));
200+
var _ = print("unwrap(Ok(999)): ");
201+
var _ = print_line(unwrap(ok_val));
202+
203+
val err_val = Err("boom");
204+
var _ = print("isError(Err(boom))? ");
205+
var _ = print_line(isError(err_val));
206+
166207
var _ = print_line("=== Demo Complete ===");

examples/input_demo.rpy

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
var _ = print_line("=== RPython Input Demo ===");
2+
var _ = print_line("This demo showcases input(), input_int(), and input_real()");
3+
var _ = print_line("");
4+
5+
var _ = print_line("--- input() ---");
6+
val name = input("Enter your name: ");
7+
var _ = print("Hello, ");
8+
var _ = print(name);
9+
var _ = print_line("!");
10+
11+
var _ = print_line("");
12+
var _ = print_line("--- input_int() ---");
13+
val age = input_int("Enter your age: ");
14+
var _ = print("In 10 years you will be ");
15+
var _ = print_line(age + 10);
16+
17+
var _ = print_line("");
18+
var _ = print_line("--- input_real() ---");
19+
val price = input_real("Enter a price: ");
20+
val tax = price * 0.1;
21+
var _ = print("Price with 10% tax: ");
22+
var _ = print_line(to_string_fixed(price + tax, 2));
23+
24+
var _ = print_line("");
25+
var _ = print_line("--- Combining inputs ---");
26+
val a = input_int("Enter first number: ");
27+
val b = input_int("Enter second number: ");
28+
var _ = print(a);
29+
var _ = print(" + ");
30+
var _ = print(b);
31+
var _ = print(" = ");
32+
var _ = print_line(a + b);
33+
34+
var _ = print_line("");
35+
var _ = print_line("=== Demo Complete ===");

src/interpreter/expression_eval.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,40 @@ pub fn eval_function_call(
560560
let mut meta_env: Environment<Expression> = Environment::new();
561561

562562
match func_name.as_str() {
563+
// tuple_get(value, index) -> any (element) or error string
564+
"tuple_get" => {
565+
if args.len() != 2 {
566+
return Err(
567+
"[Runtime Error] tuple_get expects exactly 2 arguments".into(),
568+
);
569+
}
570+
571+
let value = match eval(args[0].clone(), env)? {
572+
ExpressionResult::Value(v) => v,
573+
ExpressionResult::Propagate(e) => {
574+
return Ok(ExpressionResult::Propagate(e))
575+
}
576+
};
577+
let index = match eval(args[1].clone(), env)? {
578+
ExpressionResult::Value(v) => v,
579+
ExpressionResult::Propagate(e) => {
580+
return Ok(ExpressionResult::Propagate(e))
581+
}
582+
};
583+
584+
meta_env.map_variable("value".to_string(), false, value);
585+
meta_env.map_variable("index".to_string(), false, index);
586+
587+
let stmt = meta_fn(&mut meta_env);
588+
if let Statement::Return(expr) = stmt {
589+
Ok(ExpressionResult::Value(*expr))
590+
} else {
591+
Err(
592+
"[Runtime Error] tuple_get builtin did not return a value".into(),
593+
)
594+
}
595+
}
596+
563597
// input([prompt]) -> String
564598
"input" => {
565599
if let Some(prompt_expr) = args.get(0) {

0 commit comments

Comments
 (0)