Add object-oriented records example
This commit is contained in:
@@ -0,0 +1,50 @@
|
|||||||
|
;; Output: [150 130 "Insufficient funds" 130]
|
||||||
|
|
||||||
|
; ---------------------------------------------------------
|
||||||
|
; Object-Oriented Records Example
|
||||||
|
; ---------------------------------------------------------
|
||||||
|
; This showcase treats a record as an object by storing
|
||||||
|
; closures that capture private state.
|
||||||
|
|
||||||
|
(do
|
||||||
|
; A factory function (Constructor) for a BankAccount object
|
||||||
|
(def make-account (fn [initial-balance]
|
||||||
|
(do
|
||||||
|
; 'balance' is a captured parameter, acting as private state
|
||||||
|
(def balance initial-balance)
|
||||||
|
|
||||||
|
{
|
||||||
|
; Method: Get current balance
|
||||||
|
:balance (fn [] balance)
|
||||||
|
|
||||||
|
; Method: Deposit money
|
||||||
|
:deposit (fn [amount]
|
||||||
|
(assign balance (+ balance amount)))
|
||||||
|
|
||||||
|
; Method: Withdraw money with validation
|
||||||
|
:withdraw (fn [amount]
|
||||||
|
(if (>= balance amount)
|
||||||
|
(assign balance (- balance amount))
|
||||||
|
"Insufficient funds"))
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
|
||||||
|
; 1. Create an instance
|
||||||
|
(def my-acc (make-account 100))
|
||||||
|
|
||||||
|
; 2. Call 'deposit' method
|
||||||
|
; Note the double parens: (.deposit my-acc) gets the function, then we call it
|
||||||
|
(def b1 ((.deposit my-acc) 50)) ; balance is now 150
|
||||||
|
|
||||||
|
; 3. Call 'withdraw' method
|
||||||
|
(def b2 ((.withdraw my-acc) 20)) ; balance is now 130
|
||||||
|
|
||||||
|
; 4. Call 'withdraw' with invalid amount
|
||||||
|
(def error-msg ((.withdraw my-acc) 1000))
|
||||||
|
|
||||||
|
; 5. Call 'balance' getter
|
||||||
|
(def final-balance ((.balance my-acc)))
|
||||||
|
|
||||||
|
; Return summary of operations
|
||||||
|
[b1 b2 error-msg final-balance]
|
||||||
|
)
|
||||||
@@ -472,9 +472,12 @@ impl Optimizer {
|
|||||||
|
|
||||||
for (old_idx, capture_addr) in original_upvalues.iter().enumerate() {
|
for (old_idx, capture_addr) in original_upvalues.iter().enumerate() {
|
||||||
let mut inlined_val = None;
|
let mut inlined_val = None;
|
||||||
if let Some(val) = sub.get_value(capture_addr) {
|
if !sub.assigned.contains(capture_addr)
|
||||||
|
&& let Some(val) = sub.get_value(capture_addr)
|
||||||
|
{
|
||||||
inlined_val = Some(val.clone());
|
inlined_val = Some(val.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Address::Local(slot) = capture_addr {
|
if let Address::Local(slot) = capture_addr {
|
||||||
sub.captured_slots.insert(*slot);
|
sub.captured_slots.insert(*slot);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,9 +159,7 @@ impl UsageInfo {
|
|||||||
|
|
||||||
pub fn collect_pattern(&mut self, node: &AnalyzedNode) {
|
pub fn collect_pattern(&mut self, node: &AnalyzedNode) {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
BoundKind::Define { addr, .. } => {
|
BoundKind::Define { .. } => {}
|
||||||
self.assigned.insert(*addr);
|
|
||||||
}
|
|
||||||
BoundKind::Set { addr, .. } => {
|
BoundKind::Set { addr, .. } => {
|
||||||
self.assigned.insert(*addr);
|
self.assigned.insert(*addr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -427,6 +427,47 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_optimizer_upvalue_inlining_bug_repro() {
|
||||||
|
let env = Environment::new();
|
||||||
|
let source = r#"
|
||||||
|
(do
|
||||||
|
(def make-counter (fn [init]
|
||||||
|
(do
|
||||||
|
(def val init)
|
||||||
|
{
|
||||||
|
:inc (fn [] (assign val (+ val 1)))
|
||||||
|
:get (fn [] val)
|
||||||
|
})))
|
||||||
|
(def c (make-counter 10))
|
||||||
|
((.inc c))
|
||||||
|
((.get c)))
|
||||||
|
"#;
|
||||||
|
let res = env.run_script(source);
|
||||||
|
assert!(res.is_ok(), "Optimizer bug triggered: {:?}", res.err());
|
||||||
|
assert_eq!(format!("{}", res.unwrap()), "11");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_optimizer_destructuring_inlining_and_mutation() {
|
||||||
|
let env = Environment::new();
|
||||||
|
// 1. Test: Destructuring definition should allow inlining if not mutated
|
||||||
|
let source_inline = "(do (def [x y] [10 20]) (+ x y))";
|
||||||
|
let res_inline = env.run_script(source_inline).unwrap();
|
||||||
|
assert_eq!(format!("{}", res_inline), "30");
|
||||||
|
|
||||||
|
// 2. Test: Destructuring definition should NOT be inlined if mutated (Regression Test)
|
||||||
|
let source_mutation = r#"
|
||||||
|
(do
|
||||||
|
(def [a b] [1 2])
|
||||||
|
(def f (fn [] (assign a (+ a b))))
|
||||||
|
(f)
|
||||||
|
a)
|
||||||
|
"#;
|
||||||
|
let res_mutation = env.run_script(source_mutation).unwrap();
|
||||||
|
assert_eq!(format!("{}", res_mutation), "3");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_record_basics() {
|
fn test_record_basics() {
|
||||||
let env = Environment::new();
|
let env = Environment::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user