Working examples from the Lambda Smalltalk examples/ directory. Each file is executable as-is.

Run any example: lambda-st run examples/01_basics.st


01_basics.st

"
Lambda Smalltalk - Basics

Smalltalk is SVO: Subject Verb Object

  'hello' size
     S      V

  File read: 'config.txt'
   S    V        O

Just tell WHO to do WHAT.
"

"--- Print with newline ---"
'Hello, World!' printNl.  "→ nil (side effect: prints to stdout)"

"--- Print without newline ---"
'No ' print.      "→ nil (side effect: prints to stdout)"
'newline' print.  "→ nil"
'' printNl.

"--- Variables ---"
| x y |
x := 10.
y := 20.
(x + y) printNl.

"--- String size ---"
'hello' size printNl.  "size → Integer (5)"

"--- String uppercase ---"
'hello' asUppercase printNl.  "asUppercase → String ('HELLO')"

"--- String lowercase ---"
'HELLO' asLowercase printNl.  "asLowercase → String ('hello')"

"--- String trim ---"
'  hello  ' trim printNl.  "trim → String ('hello')"

"--- String concatenate ---"
('Hello' , ' ' , 'World') printNl.  ", → String (concatenated)"

"--- String repeat ---"
('-' * 10) printNl.  "* → String (repeated N times)"

"--- String replace ---"
('hello' replaceAll: 'l' with: 'L') printNl.  "replaceAll:with: → String"

"--- Number to string ---"
42 asString printNl.  "asString → String ('42')"

"--- String to integer ---"
('123' asInteger + 10) printNl.  "asInteger → Integer (123)"

"--- String to number ---"
'3.14' asNumber printNl.  "asNumber → Float (3.14)"

"--- Arithmetic ---"
(10 + 5) printNl.  "+ → Number"
(10 - 5) printNl.  "- → Number"
(10 * 5) printNl.  "* → Number"
(10 / 5) printNl.  "/ → Integer or Fraction"
(10 / 3) printNl.  "/ → Fraction (10/3, not 3.333...)"

"--- Exact Fraction ---"
((1 / 3) * 3) printNl.  "→ 1 (exact, no floating point error)"

"--- LargeInteger (overflow) ---"
(2147483647 + 1) printNl.           "→ LargeInteger (auto-promotes)"
(1000000000 * 1000000000) printNl.  "→ LargeInteger (10^18)"

"--- Modulo ---"
(7 \\ 2) printNl.  "\\\\ → Integer (remainder: 1)"

"--- Floor division ---"
(-7 // 2) printNl.  "// → Integer (floor: -4)"

"--- Power ---"
(2 raisedTo: 10) printNl.  "raisedTo: → Integer (1024)"

"--- Absolute value ---"
(-5) abs printNl.  "abs → Number (5)"

"--- Square root ---"
16 sqrt printNl.  "sqrt → Float (4.0)"

"--- Min / Max ---"
(3 min: 7) printNl.  "min: → Number (smaller: 3)"
(3 max: 7) printNl.  "max: → Number (larger: 7)"

"--- Clamp (limit to range) ---"
(5 clampMin: 0 max: 10) printNl.    "clampMin:max: → Number (5, within range)"
(-5 clampMin: 0 max: 10) printNl.   "clampMin:max: → Number (0, below min)"
(15 clampMin: 0 max: 10) printNl.   "clampMin:max: → Number (10, above max)"

'=== Done! ===' printNl.

02_collections.st

"
Lambda Smalltalk - Collections

Smalltalk is SVO: Subject Verb Object

  array size        - Array, tell me your size
  array first       - Array, give me your first element
  array select: []  - Array, select elements matching this

Just tell WHO to do WHAT.
"

"Load high-level collection methods"
Smalltalk fileIn: 'lib/Collections.st'.

"--- Array literal ---"
#(1 2 3) printNl.  "→ Array #(1 2 3)"

"--- Array size ---"
#(10 20 30) size printNl.  "size → Integer (3)"

"--- Array first ---"
#('a' 'b' 'c') first printNl.  "first → element ('a')"

"--- Array last ---"
#(1 2 3 4 5) last printNl.  "last → element (5)"

"--- Array at index (1-based) ---"
| arr |
arr := #(10 20 30 40 50).
(arr at: 3) printNl.  "at: → element (30)"

"--- Create array of size N ---"
| a |
a := Array new: 3.    "new: → Array (with nil elements)"
a at: 1 put: 'x'.     "at:put: → element put ('x')"
a at: 2 put: 'y'.
a at: 3 put: 'z'.
a printNl.

"--- Iterate: do ---"
| sum |
sum := 0.
#(1 2 3 4 5) do: [ :each | sum := sum + each ].  "do: → nil (side effect)"
sum printNl.

"--- Transform: collect (map) ---"
(#(1 2 3) collect: [ :x | x * 2 ]) printNl.  "collect: → Array #(2 4 6)"

"--- Filter: select ---"
(#(1 2 3 4 5 6) select: [ :x | x > 3 ]) printNl.  "select: → Array #(4 5 6)"

"--- Filter: reject ---"
(#(1 2 3 4 5 6) reject: [ :x | x > 3 ]) printNl.  "reject: → Array #(1 2 3)"

"--- Find first match: detect ---"
(#(1 2 3 4 5) detect: [ :x | x > 3 ]) printNl.  "detect: → element or nil (4)"

"--- Find index: indexOf ---"
(#(1 2 3) indexOf: 2) printNl.  "indexOf: → Integer or 0 (2)"

"--- Check contains: includes ---"
(#(1 2 3) includes: 2) printNl.  "includes: → Boolean (true)"

"--- Reduce: inject:into ---"
(#(1 2 3 4 5) inject: 0 into: [ :acc :x | acc + x ]) printNl.  "inject:into: → accumulated value (15)"
(#(1 2 3 4 5) inject: 1 into: [ :acc :x | acc * x ]) printNl.  "→ 120 (factorial)"

"--- Sum ---"
#(1 2 3 4 5) sum printNl.  "sum → Number (15)"

"--- Sort ---"
#(3 1 4 1 5 9) sort printNl.  "sort → Array (sorted ascending)"

"--- Reverse ---"
#(1 2 3) reverse printNl.  "reverse → Array #(3 2 1)"

"--- Sort by: sortBy ---"
(#(3 1 4 1 5) sortBy: [ :a :b | a < b ]) printNl.  "sortBy: → Array (custom sort)"

"--- Unique ---"
#(1 2 2 3 3 3) unique printNl.  "unique → Array #(1 2 3)"

"--- Flatten ---"
#(#(1 2) #(3 4) #(5)) flatten printNl.  "flatten → Array #(1 2 3 4 5)"

"--- Join ---"
(#('a' 'b' 'c') join: '-') printNl.  "join: → String 'a-b-c'"

"--- Is empty ---"
#() isEmpty printNl.   "isEmpty → Boolean (true)"
#(1) isEmpty printNl.  "isEmpty → Boolean (false)"

"--- Max / Min ---"
#(3 1 4 1 5) max printNl.  "max → element (5)"
#(3 1 4 1 5) min printNl.  "min → element (1)"

"--- sumOf: / maxOf: / minOf: (block version) ---"
| people |
people := #(#('Alice' 30) #('Bob' 25) #('Carol' 35)).
(people sumOf: [:p | p at: 2]) printNl.  "sumOf: → Number (90)"
(people maxOf: [:p | p at: 2]) printNl.  "maxOf: → Number (35)"
(people minOf: [:p | p at: 2]) printNl.  "minOf: → Number (25)"

"--- Average ---"
#(10 20 30 40 50) average printNl.  "average → Number (30)"

"--- Iterate with index: doWithIndex ---"
#('a' 'b' 'c') doWithIndex: [ :elem :idx |  "doWithIndex: → nil (side effect)"
    (idx asString , ': ' , elem) printNl.
].

"--- Take first N: take ---"
(#(1 2 3 4 5) take: 3) printNl.  "take: → Array #(1 2 3)"

"--- Drop first N: drop ---"
(#(1 2 3 4 5) drop: 2) printNl.  "drop: → Array #(3 4 5)"

"--- All but first: allButFirst ---"
#(1 2 3 4 5) allButFirst printNl.  "allButFirst → Array #(2 3 4 5)"

"--- All but last: allButLast ---"
#(1 2 3 4 5) allButLast printNl.  "allButLast → Array #(1 2 3 4)"

"--- Copy with element: copyWith ---"
(#(1 2 3) copyWith: 4) printNl.  "copyWith: → Array #(1 2 3 4)"

"--- Copy without element: copyWithout ---"
(#(1 2 3 2 4 2) copyWithout: 2) printNl.  "copyWithout: → Array #(1 3 4)"

"--- Partition by predicate: partition ---"
(#(1 2 3 4 5 6) partition: [ :x | x > 3 ]) printNl.  "partition: → #(#(4 5 6) #(1 2 3))"

"--- Group by key: groupBy ---"
| grouped |
grouped := #(1 2 3 4 5 6) groupBy: [ :x | x \\ 2 ].  "groupBy: → Dict"
grouped keys printNl.            "keys → Array of keys"
(grouped at: '0') printNl.       "at: → Array #(2 4 6)"
(grouped at: '1') printNl.       "at: → Array #(1 3 5)"

"--- Zip two arrays: zip ---"
(#(1 2 3) zip: #('a' 'b' 'c')) printNl.  "zip: → Array of pairs #(#(1 'a') #(2 'b') #(3 'c'))"

"--- Split into chunks: chunks ---"
(#(1 2 3 4 5) chunks: 2) printNl.  "chunks: → Array #(#(1 2) #(3 4) #(5))"

"--- Iterate with separator: do:separatedBy ---"
#(1 2 3) do: [ :x | x print ] separatedBy: [ ', ' print ].  "do:separatedBy: → nil (side effect)"
'' printNl.

"--- ifEmpty: / ifNotEmpty: ---"
#() ifEmpty: [ 'Empty array' printNl ].  "ifEmpty: → block result or self"
#(1 2 3) ifNotEmpty: [ :arr | ('Size: ' , arr size asString) printNl ].  "ifNotEmpty: → block result or self"

"Returns self if condition not met"
| result |
result := #(1 2 3) ifEmpty: [ 'was empty' ].
result printNl.  "→ #(1 2 3) (not empty, returns self)"

"--- count: ---"
(#(1 2 3 4 5) count: [ :x | x > 2 ]) printNl.  "count: → Integer (3)"

"--- String isBlank / ifBlank: / ifNotBlank: ---"
'' isBlank printNl.       "isBlank → Boolean (true)"
'   ' isBlank printNl.    "isBlank → Boolean (true, whitespace only)"
'hello' isBlank printNl.  "isBlank → Boolean (false)"

| name |
name := ''.
name ifBlank: [ 'Using default name' printNl ].  "ifBlank: → block result or self"

name := 'Alice'.
name ifNotBlank: [ :n | ('Hello, ' , n) printNl ].  "ifNotBlank: → block result or self"

"--- species: Collection operations preserve type ---"
"String select:/collect:/reject: return String (not Array)"
| letters upper noSpaces |
letters := 'Hello World' select: [ :c | c isLetter ].
letters printNl.          "select: → String 'HelloWorld'"
letters class printNl.    "class → String"

upper := 'hello' collect: [ :c | c asUppercase ].
upper printNl.            "collect: → String 'HELLO'"

noSpaces := 'a b c' reject: [ :c | c = $  ].
noSpaces printNl.         "reject: → String 'abc'"

"Array operations return Array"
(#(1 2 3 4 5) select: [ :n | n odd ]) class printNl.  "class → Array"

"Interval species is Array (intervals are contiguous ranges)"
((1 to: 10) select: [ :n | n > 5 ]) printNl.  "select: → Array #(6 7 8 9 10)"
((1 to: 5) collect: [ :n | n * n ]) printNl.  "collect: → Array #(1 4 9 16 25)"

'=== Done! ===' printNl.

03_control_flow.st

"
Lambda Smalltalk - Control Flow

Smalltalk is SVO: Subject Verb Object

  (x > 0) ifTrue: [ ... ]
     S       V       O

  5 timesRepeat: [ ... ]
  S      V          O

Just tell WHO to do WHAT.
"

"--- If true ---"
(3 > 0) ifTrue: [ 'positive' printNl ].  "ifTrue: → block result or nil"

"--- If false ---"
(3 < 0) ifFalse: [ 'not negative' printNl ].  "ifFalse: → block result or nil"

"--- If-then-else ---"
| x |
x := 10.
(x > 5)
    ifTrue: [ 'big' printNl ]
    ifFalse: [ 'small' printNl ].  "ifTrue:ifFalse: → block result"

"--- Equality ---"
(5 = 5) printNl.   "= → Boolean (true)"
(5 ~= 3) printNl.  "~= → Boolean (true, not equal)"

"--- Comparison ---"
(3 < 5) printNl.   "< → Boolean (true)"
(5 > 3) printNl.   "> → Boolean (true)"
(3 <= 3) printNl.  "<= → Boolean (true)"
(5 >= 5) printNl.  ">= → Boolean (true)"

"--- Logical AND / OR / NOT ---"
(true & true) printNl.   "& → Boolean (true, AND)"
(true | false) printNl.  "| → Boolean (true, OR)"
true not printNl.        "not → Boolean (false)"

"--- Repeat N times ---"
| count |
count := 0.
5 timesRepeat: [ count := count + 1 ].  "timesRepeat: → nil"
count printNl.

"--- For loop: to:do ---"
| sum |
sum := 0.
1 to: 10 do: [ :i | sum := sum + i ].  "to:do: → nil"
sum printNl.

"--- For loop with step: to:by:do ---"
| evens |
evens := 0.
2 to: 10 by: 2 do: [ :i | evens := evens + i ].  "to:by:do: → nil"
evens printNl.

"--- While true ---"
| n |
n := 1.
[ n < 100 ] whileTrue: [ n := n * 2 ].  "whileTrue: → nil"
n printNl.

"--- While false ---"
| m |
m := 10.
[ m = 0 ] whileFalse: [ m := m - 1 ].  "whileFalse: → nil"
m printNl.

"--- If nil ---"
| val |
val := nil.
val ifNil: [ 'was nil' printNl ].  "ifNil: → block result or self"

"--- If not nil ---"
val := 42.
val ifNotNil: [ 'not nil' printNl ].  "ifNotNil: → block result or nil"

"--- Nested if-else ---"
| score grade |
score := 85.
grade := (score >= 90)
    ifTrue: [ 'A' ]
    ifFalse: [
        (score >= 80)
            ifTrue: [ 'B' ]
            ifFalse: [ 'C' ]
    ].  "→ 'B' (returns evaluated block result)"
grade printNl.

'=== Done! ===' printNl.

04_blocks.st

"
Lambda Smalltalk - Blocks and Closures

Blocks are Smalltalk's lambdas/closures.
They are first-class objects that hold code.

  block value           - Block, execute yourself
  block value: 5        - Block, execute with argument 5
  array collect: block  - Array, transform using this block

Just tell WHO to do WHAT.
"

"--- Create and execute a block ---"
| b |
b := [ 'Hello from block' printNl ].  "→ Block object"
b value.  "value → last expression result (nil from printNl)"

"--- Block with one argument ---"
| add1 |
add1 := [ :x | x + 1 ].
(add1 value: 5) printNl.  "value: → block result (6)"

"--- Block with two arguments ---"
| add |
add := [ :a :b | a + b ].
(add value: 3 value: 4) printNl.  "value:value: → block result (7)"

"--- Closure: captures outer variable ---"
| counter block |
counter := 0.
block := [ counter := counter + 1. counter ].  "captures 'counter' by reference"
(block value) printNl.  "→ 1"
(block value) printNl.  "→ 2"
(block value) printNl.  "→ 3"

"--- Block as function ---"
| square |
square := [ :n | n * n ].
(square value: 7) printNl.  "→ 49"

"--- Block as callback ---"
| nums doubled |
nums := #(1 2 3 4 5).
doubled := nums collect: [ :x | x * 2 ].  "collect: → Array #(2 4 6 8 10)"
doubled printNl.

"--- Nested blocks ---"
| outer |
outer := [
    | inner |
    inner := [ 'inner' ].
    inner value
].
(outer value) printNl.  "→ 'inner'"

"--- Higher-order: block takes block ---"
| twice result |
twice := [ :f :x | f value: (f value: x) ].  "applies f twice"
result := twice value: [ :n | n + 1 ] value: 5.  "→ 7 (5+1+1)"
result printNl.

'=== Done! ===' printNl.

05_classes.st

"
Lambda Smalltalk - Classes

Smalltalk is SVO: Subject Verb Object

  Object subclass: #Counter ...  - Object, create a subclass named Counter
  Counter new                    - Counter, create a new instance
  counter inc                    - Counter instance, increment yourself

Just tell WHO to do WHAT.
"

"--- Define a class ---"
Object subclass: #Counter instanceVariableNames: 'value'.  "→ Class object"

"--- Define methods ---"
Counter >> initialize  "called by 'new', → self"
    value := 0.
!

Counter >> inc  "→ Integer (incremented value)"
    value := value + 1.
    ^ value
!

Counter >> value  "→ Integer (current value)"
    ^ value
!

"--- Create and use instance ---"
| c |
c := Counter new.  "new → Counter instance"
c inc printNl.     "→ 1"
c inc printNl.     "→ 2"
c inc printNl.     "→ 3"
c value printNl.   "→ 3"

"--- Built-in: Point ---"
| p |
p := 3 @ 4.    "@ → Point (3@4)"
p x printNl.   "x → Number (3)"
p y printNl.   "y → Number (4)"
p printNl.     "→ '3@4'"

'=== Done! ===' printNl.

06_file_io.st

"
Lambda Smalltalk - File I/O

Smalltalk is SVO: Subject Verb Object

  File read: 'config.txt'                      - File, read this path (returns string)
  File write: 'out.txt' content: x             - File, write x to this path
  File exists: 'config.txt'                    - File, does this path exist?
  LazyFileStream linesDo: path do: [:line|..]  - Zero-copy line iteration

Just tell WHO to do WHAT.
"

"--- Read file ---"
| content |
content := File read: 'examples/06_file_io.st'.  "read: → String (file contents)"
(content size) printNl.

"--- Write file ---"
File write: 'examples/temp.txt' content: 'Hello, File!'.  "write:content: → true"
(File read: 'examples/temp.txt') printNl.

"--- Check file exists ---"
(File exists: 'examples/temp.txt') printNl.           "exists: → Boolean (true)"
(File exists: 'examples/no_such_file.xyz') printNl.   "exists: → Boolean (false)"

"--- Get file size ---"
| fileSize |
fileSize := (File size: 'examples/temp.txt').          "size: → Integer (bytes) or nil"
('File size: ' , fileSize asString , ' bytes') printNl.

"--- Glob pattern ---"
| files |
files := File glob: 'examples/*.st'.  "glob: → Array of file paths"
files size printNl.

"--- Delete file ---"
(File delete: 'examples/temp.txt') printNl.  "delete: → true (error on failure)"
(File exists: 'examples/temp.txt') printNl.  "→ false"

"--- Binary file handling ---"
"Note: File read: returns nil for binary files (non-UTF8)"
| binaryResult |
binaryResult := (File read: 'target/debug/lambda-st.exe').
binaryResult isNil
    ifTrue: ['Binary file returned nil (expected behavior)' printNl]
    ifFalse: ['Unexpected: binary file read as text' printNl].

"--- Binary file I/O with ByteArray ---"
| binData readBin |
binData := ByteArray new: 5.
binData at: 1 put: 0.
binData at: 2 put: 255.
binData at: 3 put: 65.   "ASCII 'A'"
binData at: 4 put: 127.
binData at: 5 put: 10.   "LF"

"Write binary file"
(File writeBinary: 'examples/temp_binary.bin' content: binData).

"Read binary file (returns ByteArray)"
readBin := (File readBinary: 'examples/temp_binary.bin').
('Binary file size: ' , readBin size asString , ' bytes') printNl.
('First byte: ' , (readBin at: 1) asString) printNl.
('Last byte: ' , (readBin at: 5) asString) printNl.

"Convert ByteArray to String (if UTF-8)"
| utf8Bytes utf8Text |
utf8Bytes := ByteArray fromString: 'Hello, 世界!'.
(File writeBinary: 'examples/temp_utf8.bin' content: utf8Bytes).
utf8Text := (File readBinary: 'examples/temp_utf8.bin') asString.
('UTF-8 roundtrip: ' , utf8Text) printNl.

"Cleanup"
(File delete: 'examples/temp_binary.bin').
(File delete: 'examples/temp_utf8.bin').

"--- Efficient line iteration with LazyFileStream ---"
"Zero-copy mmap-based streaming - lines are borrowed strings"
| lineCount |
lineCount := 0.
LazyFileStream linesDo: 'examples/06_file_io.st' do: [:line |
    lineCount := lineCount + 1.
].
('Lines in this file: ' , lineCount asString) printNl.

'=== Done! ===' printNl.

07_file_in.st

"=== Lambda Smalltalk: Smalltalk fileIn: ==="

'=== Load library ===' printNl.

Smalltalk fileIn: 'examples/lib/math.st'.  "fileIn: → nil (loads and executes file)"

'=== Use loaded class ===' printNl.

| calc |
calc := Calculator new.  "new → Calculator instance"
calc setValue: 7.        "setValue: → self"
calc square printNl.     "square → Integer (49)"
calc value printNl.      "value → Integer (7)"

'=== Done! ===' printNl.

08_plugins.st

"=== Lambda Smalltalk: Plugin System (FFI) ==="
"Load and call native plugins written in Rust (.dll/.so/.dylib)"
"Plugins are searched in: plugins/ directory relative to executable"

"=== 1. UUID Plugin ==="
'--- UUID Plugin ---' printNl.
Plugin load: 'uuid'.
'Loaded plugins: ' print. (Plugin list) printNl.
'Available functions: ' print. (Plugin functions: 'uuid') printNl.

"Generate UUIDs"
| id1 id2 |
id1 := Plugin call: 'uuid' function: 'v4' args: #().
'UUID v4: ' print. id1 printNl.

id2 := Plugin call: 'uuid' function: 'v7' args: #().
'UUID v7: ' print. id2 printNl.

"Validate & inspect"
'is_valid(id1): ' print. (Plugin call: 'uuid' function: 'is_valid' args: { id1 }) printNl.
'is_valid(bad):  ' print. (Plugin call: 'uuid' function: 'is_valid' args: #('not-a-uuid')) printNl.
'version(id1):  ' print. (Plugin call: 'uuid' function: 'version' args: { id1 }) printNl.
'parse(id2):    ' print. (Plugin call: 'uuid' function: 'parse' args: { id2 }) printNl.

"=== 2. SysInfo Plugin ==="
'' printNl.
'--- SysInfo Plugin ---' printNl.
Plugin load: 'sysinfo'.
'Loaded plugins: ' print. (Plugin list) printNl.

"System info"
'Host name:    ' print. (Plugin call: 'sysinfo' function: 'host_name' args: #()) printNl.
'OS version:   ' print. (Plugin call: 'sysinfo' function: 'os_version' args: #()) printNl.
'CPU count:    ' print. (Plugin call: 'sysinfo' function: 'cpu_count' args: #()) printNl.
'Physical cores: ' print. (Plugin call: 'sysinfo' function: 'physical_core_count' args: #()) printNl.

"Memory (bytes)"
| total used |
total := Plugin call: 'sysinfo' function: 'total_memory' args: #().
used  := Plugin call: 'sysinfo' function: 'used_memory' args: #().
'Total memory: ' print. total printNl.
'Used memory:  ' print. used printNl.

"=== 3. Log Plugin ==="
'' printNl.
'--- Log Plugin ---' printNl.
Plugin load: 'log'.

"Default config: stderr, JSON format, info level"
Plugin call: 'log' function: 'init' args: #().

"Basic log levels"
Plugin call: 'log' function: 'info' args: #('Server started on port 8080').
Plugin call: 'log' function: 'warn' args: #('Slow query detected').
Plugin call: 'log' function: 'debug' args: #('This is filtered out (level=info)').

"Structured fields"
Plugin call: 'log' function: 'info_with' args: {
    'Request processed'.
    '{"method":"GET","path":"/api/users","status":200,"duration_ms":42}' }.

"Persistent context (e.g., request ID)"
Plugin call: 'log' function: 'set_context' args: #('{"request_id":"req-abc-123"}').
Plugin call: 'log' function: 'info' args: #('Processing payment').
Plugin call: 'log' function: 'clear_context' args: #().

"Text format"
Plugin call: 'log' function: 'init' args: #('{"format":"text"}').
Plugin call: 'log' function: 'info' args: #('Human-readable text output').
Plugin call: 'log' function: 'error_with' args: { 'Disk full'. '{"disk":"/dev/sda1","usage_pct":99}' }.

Plugin call: 'log' function: 'flush' args: #().

'=== Done! ===' printNl.

09_modules.st

"=== Module System Example ==="
"Lambda Smalltalk supports a module system for namespace management."

"--- Basic Usage (without explicit modules) ---"
"When no module is declared, all classes are in the 'Core' module."
"This maintains backward compatibility with existing code."

Object subclass: #SimplePoint instanceVariableNames: 'x y'.  "→ Class object"

SimplePoint >> x  "→ Number (x value)"
    ^x
!

SimplePoint >> y  "→ Number (y value)"
    ^y
!

SimplePoint >> x: aValue  "→ self"
    x := aValue
!

SimplePoint >> y: aValue  "→ self"
    y := aValue
!

SimplePoint >> initialize  "→ self"
    x := 0. y := 0
!

|p|
p := SimplePoint new.
p x: 10.
p y: 20.
'SimplePoint x:' printNl.
p x printNl.
'SimplePoint y:' printNl.
p y printNl.

"--- Module Import ---"
"Import modules from lib/{ModuleName}/{ModuleName}.st"
"The module is auto-loaded on first import."

Module import: 'CodeGen'.  "import: → nil (loads module into namespace)"

"Now we can use String extensions defined in CodeGen module"
'camelToSnake test:' printNl.
('myVariableName' camelToSnake) printNl.  "camelToSnake → String ('my_variable_name')"

'snakeToCamel test:' printNl.
('my_variable_name' snakeToCamel) printNl.  "snakeToCamel → String ('myVariableName')"

"--- Module Definition ---"
"In a module file (e.g., lib/MyLib/MyLib.st):"
"
Module name: 'MyLib'.
Module export: #(PublicClass1 PublicClass2).

Object subclass: #PublicClass1 instanceVariableNames: ''.
Object subclass: #PrivateHelper instanceVariableNames: ''.
"

'=== Module example done ===' printNl.

10_lazy_collections.st

"
Lambda Smalltalk - Lazy Collections & Streaming I/O Examples

Demonstrates two approaches to memory-efficient processing:

1. Lazy Collections (new in 2026-01)
   - Chain operations without intermediate arrays
   - Short-circuit evaluation with take:/detect:
   - O(1) memory for transformations

2. Streaming I/O
   - Process files line-by-line with LazyFileStream linesDo:do:
   - Constant memory regardless of file size
"

Module import: 'Lazy'.

'=== Lazy Collections ===' printNl.
'' printNl.

" ============================================ "
" Example 1: Basic Lazy Operations              "
" ============================================ "

'--- Example 1: Eager vs Lazy ---' printNl.

"Eager approach - creates 2 intermediate arrays:"
| eagerResult |
eagerResult := ((1 to: 10) select: [:x | x odd]) collect: [:x | x * 10].
('Eager: ' , eagerResult asString) printNl.

"Lazy approach - no intermediate arrays:"
| lazyResult |
lazyResult := ((((1 to: 10) asLazy) select: [:x | x odd]) collect: [:x | x * 10]) force.
('Lazy:  ' , lazyResult asString) printNl.
'' printNl.

" ============================================ "
" Example 2: Short-circuit with take:           "
" ============================================ "

'--- Example 2: Short-circuit evaluation ---' printNl.

"Find first 5 odd numbers from 1 to 1,000,000"
"Lazy: stops after finding 5, processes only ~10 elements"
| first5odd |
first5odd := ((((1 to: 1000000) asLazy) select: [:x | x odd]) take: 5) force.
('First 5 odd numbers: ' , first5odd asString) printNl.
'' printNl.

" ============================================ "
" Example 3: Complex chain                      "
" ============================================ "

'--- Example 3: Complex transformation chain ---' printNl.

| data processed |
data := #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15).

"Chain: filter > 5, multiply by 2, take first 4"
processed := (((((data asLazy) select: [:x | x > 5]) collect: [:x | x * 2]) take: 4) force).
('Original: ' , data asString) printNl.
('Processed (>5, *2, take 4): ' , processed asString) printNl.
'' printNl.

" ============================================ "
" Example 4: Aggregation                        "
" ============================================ "

'--- Example 4: Lazy aggregation ---' printNl.

"Sum of odd numbers 1-100 (using force + eager sum)"
| nums1 filtered1 oddArray sumOfOdds |
nums1 := (1 to: 100) asLazy.
filtered1 := nums1 select: [:x | x odd].
oddArray := filtered1 force.
sumOfOdds := 0.
oddArray do: [:x | sumOfOdds := sumOfOdds + x].
('Sum of odd numbers 1-100: ' , sumOfOdds asString) printNl.

"Count numbers over 50"
| nums2 filtered2 countOver50 |
nums2 := (1 to: 100) asLazy.
filtered2 := nums2 select: [:x | x > 50].
countOver50 := filtered2 count.
('Count of numbers > 50: ' , countOver50 asString) printNl.
'' printNl.

" ============================================ "
" Example 5: detect: with lazy                  "
" ============================================ "

'--- Example 5: Find first match ---' printNl.

| firstBig |
firstBig := ((1 to: 1000000) asLazy) detect: [:x | x > 999].
('First number > 999: ' , firstBig asString) printNl.
'' printNl.

" ============================================ "
" Example 6: Streaming I/O (line-by-line)       "
" ============================================ "

'--- Example 6: Streaming file processing ---' printNl.

"Create test file"
File write: 'test_stream.txt' content: 'hello
world
lambda
smalltalk
programming'.

"Stream: filter + transform in single pass"
'Lines longer than 5 chars (uppercase):' printNl.
LazyFileStream linesDo: 'test_stream.txt' do: [:line |
    (line size > 5) ifTrue: [
        ('  ' , line asUppercase) printNl
    ]
].
'' printNl.

"Count lines without loading entire file"
| lineCount |
lineCount := 0.
LazyFileStream linesDo: 'test_stream.txt' do: [:line | lineCount := lineCount + 1].
('Total lines: ' , lineCount asString) printNl.

"Cleanup"
File delete: 'test_stream.txt'.
'' printNl.

" ============================================ "
" Example 7: When to use Lazy vs Eager          "
" ============================================ "

'--- Example 7: Choosing the right approach ---' printNl.

"LAZY is better when:"
"- Processing large collections (>1000 elements)"
"- Using take:/detect: (short-circuit)"
"- Chaining multiple operations"
"- Memory is a concern"

"EAGER is better when:"
"- Small collections (<100 elements)"
"- Single operation (no chaining)"
"- Need to iterate multiple times"
"- Simplicity is priority"

'For small data: prefer eager (simpler)' printNl.
| small |
small := #(1 2 3 4 5) select: [:x | x odd].
('Small eager: ' , small asString) printNl.

'For large data + short-circuit: prefer lazy' printNl.
| large |
large := ((((1 to: 1000000) asLazy) select: [:x | (x \\ 7) = 0]) take: 3) force.
('Large lazy (first 3 multiples of 7): ' , large asString) printNl.

'' printNl.
'=== Examples completed! ===' printNl.

11_manual_gc.st

"======================================
 Manual Garbage Collection Examples
======================================"

'=== GC Basics ===' printNl.

"Test 1: Create objects without GC"
'Creating 1000 objects...' printNl.
1 to: 1000 do: [:i |  "to:do: → nil"
  arr := Array new: 10.  "new: → Array (10 slots)"
  arr at: 1 put: i.      "at:put: → element"
].
'Done.' printNl.
'' printNl.

"Test 2: Check heap size"
'=== Heap Size ===' printNl.
'Current heap size: ' print. System heapSize printNl.
'GC threshold: ' print. System gcThreshold printNl.
'' printNl.

"Test 3: GC with live objects"
'=== GC Test ===' printNl.
'Creating some objects that stay alive...' printNl.
liveData := OrderedCollection new.  "new → empty OrderedCollection"
1 to: 100 do: [:i |
  liveData add: (Array new: 5).  "add: → self"
].

'Creating temporary objects...' printNl.
1 to: 1000 do: [:i |
  temp := Array new: 10.
].

'Live objects: ' print. liveData size printNl.
'' printNl.

"Test 4: Memory usage pattern"
'=== Memory Pattern ===' printNl.
'This test creates and discards objects in a loop' printNl.
1 to: 10 do: [:round |
  ('Round ' , round asString) printNl.
  1 to: 100 do: [:i |
    temp := Array new: 20.
    temp at: 1 put: i.
  ].
].
'Done with 10 rounds (1000 temp objects created)' printNl.
'' printNl.

'=== All Tests Complete ===' printNl.

12_utilities.st

"============================================
 12_utilities.st - Utility Classes Demo
 ============================================
 Demonstrates: Json, Yaml, Toml, Csv, Env, Random, DateTime, Path, Sha2, Hmac, Base64,
               Directory, System, Http, Regex
"

'=== Json ===' printNl.

"Parse JSON string to Dictionary"
| json data |
json := '{"name": "Alice", "age": 30}'.
data := Json parse: json.
('Parsed: ' , (data at: 'name') , ', age=' , (data at: 'age') asString) printNl.

"Generate JSON from Dictionary"
| dict output |
dict := Dict new.
dict at: 'city' put: 'Tokyo'.
dict at: 'population' put: 14000000.
output := Json generate: dict.
('Generated: ' , output) printNl.

"Dictionary merge: - merge another dictionary (overwrites existing keys)"
| base override merged |
base := Dict new.
base at: 'host' put: 'localhost'.
base at: 'port' put: 8080.

override := Dict new.
override at: 'port' put: 3000.
override at: 'debug' put: true.

merged := base merge: override.
('Merged config - host: ' , (merged at: 'host') , ', port: ' , (merged at: 'port') asString , ', debug: ' , (merged at: 'debug') asString) printNl.


'=== Yaml ===' printNl.

"Parse YAML string to Dictionary"
| yaml yamlData |
yaml := 'name: Bob
age: 25
city: Osaka'.
yamlData := Yaml parse: yaml.
('Parsed YAML: name=' , (yamlData at: 'name') , ', age=' , (yamlData at: 'age') asString) printNl.


'=== Toml ===' printNl.

"Parse TOML string to Dictionary"
| toml tomlData |
toml := 'title = "Lambda Smalltalk"
version = "0.1.0"

[author]
name = "Developer"'.
tomlData := Toml parse: toml.
('Parsed TOML: title=' , (tomlData at: 'title')) printNl.


'=== Csv ===' printNl.

"Parse CSV string - returns array of dictionaries (header row becomes keys)"
| csv rows row |
csv := 'name,age,city
Alice,30,Tokyo
Bob,25,Osaka
Charlie,35,Kyoto'.
rows := Csv parse: csv.
('CSV data rows: ' , rows size asString) printNl.
"Each row is a Dictionary with header keys"
row := rows at: 1.
('Row 1: name=' , (row at: 'name') , ', age=' , (row at: 'age') asString , ', city=' , (row at: 'city')) printNl.
row := rows at: 2.
('Row 2: name=' , (row at: 'name') , ', age=' , (row at: 'age') asString , ', city=' , (row at: 'city')) printNl.

"Streaming CSV - memory efficient for large files"
| reader names |
reader := Csv reader: 'product,price
Apple,100
Banana,80
Orange,120'.
names := OrderedCollection new.
[reader atEnd] whileFalse: [
    | r |
    r := reader next.
    r ifNotNil: [names add: (r at: 'product')]
].
('Streamed products: ' , (names join: ', ')) printNl.


'=== Env (Environment Variables) ===' printNl.

"Get environment variable"
| path |
path := Env at: 'PATH'.
(path isNil)
    ifTrue: [ 'PATH not set' printNl ]
    ifFalse: [ ('PATH length: ' , path size asString) printNl ].

"Set and get environment variable"
Env at: 'LAMBDA_ST_TEST' put: 'hello'.
('LAMBDA_ST_TEST = ' , (Env at: 'LAMBDA_ST_TEST')) printNl.

"List all environment variable names"
| keys |
keys := Env keys.
('Total env vars: ' , keys size asString) printNl.


'=== Random ===' printNl.

"Generate random float [0.0, 1.0)"
| r |
r := Random next.
('Random float: ' , r asString) printNl.

"Generate random integer [0, max)"
| n |
n := Random nextInt: 100.
('Random int (0-99): ' , n asString) printNl.

"Generate 5 random numbers"
5 timesRepeat: [
    (Random nextInt: 10) print. ' ' print.
].
'' printNl.


'=== DateTime ===' printNl.

"Get current timestamp"
| now |
now := DateTime now.
('Now (epoch): ' , now asString) printNl.

"Format timestamp"
| formatted |
formatted := now format: '%Y-%m-%d %H:%M:%S'.
('Formatted: ' , formatted) printNl.

"Parse date string (RFC3339 format)"
| parsed |
parsed := DateTime parse: '2025-12-25T10:30:00+00:00'.
(parsed isNil)
    ifTrue: [ 'Failed to parse' printNl ]
    ifFalse: [ ('Parsed timestamp: ' , parsed asString) printNl ].


'=== Path ===' printNl.

"Extract basename"
| basename |
basename := Path basename: '/home/user/documents/report.pdf'.
('Basename: ' , basename) printNl.

"Extract dirname"
| dirname |
dirname := Path dirname: '/home/user/documents/report.pdf'.
('Dirname: ' , dirname) printNl.

"Extract extension"
| ext |
ext := Path extension: '/home/user/documents/report.pdf'.
('Extension: ' , ext) printNl.

"Join paths"
| joined |
joined := Path join: '/home/user' with: 'documents'.
('Joined: ' , joined) printNl.

"Get absolute path"
| abs |
abs := Path absolute: 'examples'.
('Absolute: ' , abs) printNl.

"Check if path exists"
| exists |
exists := Path exists: '.'.
('Current dir exists: ' , exists asString) printNl.

exists := Path exists: '/nonexistent/path/that/does/not/exist'.
('Nonexistent path exists: ' , exists asString) printNl.

"Check if path is directory"
| isDir |
isDir := Path isDirectory: '.'.
('Current dir is directory: ' , isDir asString) printNl.

isDir := Path isDirectory: 'examples/11_utilities.st'.
('This file is directory: ' , isDir asString) printNl.


'=== Sha2 (Hashing) ===' printNl.

"SHA256 hash"
| hash |
hash := Sha2 sha256: 'hello world'.
('SHA256: ' , hash) printNl.


'=== Hmac (Message Authentication) ===' printNl.

"HMAC-SHA256 for API signatures and webhook verification"
| signature |
signature := Hmac sha256: 'message to sign' key: 'secret-key'.
('HMAC-SHA256: ' , signature) printNl.

"AWS-style signature (simplified example)"
| awsSecret kDate |
awsSecret := 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'.
kDate := Hmac sha256: '20260110' key: ('AWS4' , awsSecret).
('AWS kDate: ' , kDate) printNl.


'=== Base64 ===' printNl.

"Encode to Base64"
| encoded decoded |
encoded := Base64 encode: 'Hello, Lambda Smalltalk!'.
('Encoded: ' , encoded) printNl.

"Decode from Base64"
decoded := Base64 decode: encoded.
('Decoded: ' , decoded) printNl.


'=== Directory ===' printNl.

"List directory entries - returns [name, isDir, size, mtime] tuples"
| entries |
entries := Directory entries: 'examples'.
('Entries in examples/: ' , entries size asString , ' items') printNl.
entries do: [:entry |
    | name isDir size mtime |
    name  := entry at: 1.
    isDir := entry at: 2.
    size  := entry at: 3.
    mtime := entry at: 4.
    isDir
        ifTrue: [ ('  [DIR] ' , name , ' (mtime: ' , mtime asString , ')') printNl ]
        ifFalse: [ ('  ' , name , ' (' , size asString , ' bytes, mtime: ' , mtime asString , ')') printNl ].
].


'=== System ===' printNl.

"Dynamic class lookup (DI pattern)"
| cls obj |
cls := System classNamed: 'OrderedCollection'.
cls printNl.  "=> OrderedCollection class"
obj := cls new.
obj add: 'dynamic'.
obj add: 'instantiation'.
('Created via classNamed: ' , obj asArray asString) printNl.

"Non-existent class returns nil"
(System classNamed: 'NonExistent') isNil ifTrue: [
    'NonExistent class not found (as expected)' printNl
].

"Platform detection"
('Platform: ' , System platform) printNl.


'=== Regex (String match:) ===' printNl.

"String match: uses regex patterns"
('abc123' match: '\d+') printNl.  "=> true (contains digits)"
('abc' match: '\d+') printNl.     "=> false (no digits)"

"Email validation"
| email |
email := 'test@example.com'.
(email match: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
    ifTrue: [ (email , ' is valid email') printNl ]
    ifFalse: [ (email , ' is invalid email') printNl ].

"Pattern matching examples"
('hello123world' match: '[0-9]+') printNl.  "=> true"
('no-numbers' match: '[0-9]+') printNl.     "=> false"


'=== Http (commented - requires network) ===' printNl.

"HTTP examples are commented out since they require network access.
Uncomment to test with actual endpoints.

--- Basic GET ---
| response |
response := Http get: 'https://httpbin.org/get'.
response printNl.

--- POST with body ---
| postResponse |
postResponse := Http post: 'https://httpbin.org/post' body: jsonBody.
postResponse printNl.

--- GET with custom headers ---
| headers authResponse |
headers := Dict new.
headers at: 'Authorization' put: 'Bearer your-token-here'.
headers at: 'User-Agent' put: 'Lambda-Smalltalk/1.0'.
authResponse := Http get: 'https://api.example.com/data' headers: headers.
authResponse printNl.

--- PUT and DELETE ---
Http put: url body: data headers: headers.
Http delete: url headers: headers.
"

'(Http examples are commented - uncomment to test with network)' printNl.


'=== Done! ===' printNl.

13_transformer.st

"
Transformer Example - Source Code Surgery with Named Captures

This example demonstrates how to use the Transformer class for
source code manipulation with automatic gap preservation.
"

"Load the Transformer library"
Module import: 'Transform'.

'=== Transformer Example ===' printNl.
'' printNl.


"--- Example 1: Simple function renaming with captures ---"
'Example 1: Rename print() to console_log()' printNl.

| g t source result |

"Grammar with named captures: @func and @args"
g := Grammar from: '
    call: @func FUNC "(" @args ARGS ")"
    FUNC: /[a-zA-Z_][a-zA-Z0-9_]*/
    ARGS: /[^)]*/
    %ignore /\s+/
'.

t := Transformer new grammar: g.

"Rule receives match dict with captures"
t rule: 'call' do: [:m |
    | func args |
    func := m at: 'func'.
    args := m at: 'args'.
    (func = 'print')
        ifTrue: [ 'console_log(' , args , ')' ]
        ifFalse: [ func , '(' , args , ')' ]
].

source := 'x = print(hello); y = other(world); z = print(foo)'.
result := t transform: source.

('  Input:  ' , source) printNl.
('  Output: ' , result) printNl.
'' printNl.


"--- Example 2: Using Grammar replace:in:with: ---"
'Example 2: Using Grammar replace:in:with:' printNl.

| g2 source2 result2 |

"Grammar with captures for receiver and method"
"Note: %ignore is not needed here - selective parsing automatically skips non-matching text"
g2 := Grammar from: '
    dotcall: @recv RECV "." @method METHOD "()"
    RECV: /[a-z]+/
    METHOD: /[a-z]+/
'.

source2 := '/* comment */ obj.fetch() and client.send() /* end */'.
result2 := Grammar replace: g2 in: source2 with: [:m |
    | recv method |
    recv := m at: 'recv'.
    method := m at: 'method'.
    'await ' , recv , '.' , method , '()'
].

('  Input:  ' , source2) printNl.
('  Output: ' , result2) printNl.
'' printNl.


"--- Example 3: Inspect captures ---"
'Example 3: Inspect captures' printNl.

| g3 matches |

"Grammar with multiple captures"
g3 := Grammar from: '
    expr: @func IDENT "(" @arg IDENT ")"
    IDENT: /[a-z]+/
    %ignore /\s+/
'.

matches := Grammar findAll: g3 in: 'foo(bar)'.
matches do: [:m |
    ('  text: ' , (m at: 'text')) printNl.
    ('  func capture: ' , (m at: 'func')) printNl.
    ('  arg capture: ' , (m at: 'arg')) printNl.
].
'' printNl.


'=== Done ===' printNl.

14_streaming_io.st

"
Lambda Smalltalk - Streaming I/O Examples

This file demonstrates memory-efficient file processing using LazyFileStream.
Unlike File read: which loads entire files into memory, LazyFileStream
processes files line-by-line using mmap for O(1) memory usage.

IMPORTANT: Lines from LazyFileStream are 'borrowed' strings.
- You CANNOT store them directly in collections
- Use 'line copy' if you need to keep a line
- This trade-off enables zero-allocation iteration
"

'=== Streaming File I/O Examples ===' printNl.
'' printNl.

" ============================================ "
" Example 1: LazyFileStream linesDo:do:       "
" ============================================ "

'--- Example 1: LazyFileStream basic usage ---' printNl.

" Create a sample file "
File write: 'sample.txt' content: 'Apple
Banana
Cherry
Date
Elderberry'.

" Process each line without loading entire file "
LazyFileStream linesDo: 'sample.txt' do: [:line |
    ('  * ' , line) printNl.
].
'' printNl.


" ============================================== "
" Example 2: Filter lines while streaming        "
" ============================================== "

'--- Example 2: Filter long lines ---' printNl.

" Create file with mixed line lengths "
File write: 'mixed.txt' content: 'Short
This is a longer line
Tiny
Another very long line here
OK'.

" Process only lines longer than 10 characters "
LazyFileStream linesDo: 'mixed.txt' do: [:line |
    (line size > 10) ifTrue: [
        ('  Long: ' , line) printNl.
    ].
].
'' printNl.


" ============================================== "
" Example 3: Count matching lines efficiently   "
" ============================================== "

'--- Example 3: Count lines containing letter ---' printNl.

| matchCount |
matchCount := 0.

LazyFileStream linesDo: 'sample.txt' do: [:line |
    " Count lines containing 'e' "
    (line includes: 'e') ifTrue: [
        matchCount := matchCount + 1.
    ].
].

('Lines containing "e": ' , matchCount asString) printNl.
'' printNl.


" ============================================== "
" Example 4: Collect lines (with copy)          "
" ============================================== "

'--- Example 4: Collecting lines (need copy) ---' printNl.

| longLines |
longLines := OrderedCollection new.

LazyFileStream linesDo: 'mixed.txt' do: [:line |
    (line size > 10) ifTrue: [
        longLines add: line copy.  "IMPORTANT: copy is required!"
    ].
].

'Collected long lines:' printNl.
longLines do: [:l | ('  ' , l) printNl].
'' printNl.


" ============================================== "
" Example 5: Compare memory efficiency          "
" ============================================== "

'--- Example 5: Memory efficiency comparison ---' printNl.

" Create a larger file "
| content i count |
content := ''.
i := 1.
[ i <= 100 ] whileTrue: [
    content := content , 'Line ' , i asString , '
'.
    i := i + 1.
].
File write: 'large.txt' content: content.

'Rich-man programming (loads entire file):' printNl.
| lines |
lines := (File read: 'large.txt') lines.
('  Loaded ' , lines size asString , ' lines into memory') printNl.

'Streaming approach (LazyFileStream - zero allocation):' printNl.
count := 0.
LazyFileStream linesDo: 'large.txt' do: [:ln | count := count + 1].
('  Processed ' , count asString , ' lines without loading all') printNl.
'' printNl.


" ============================================== "
" Example 6: Pairwise line comparison           "
" ============================================== "

'--- Example 6: linesWithPreviousDo:do: (pairwise iteration) ---' printNl.

" Create a file with numbered lines "
File write: 'pairwise.txt' content: 'Line 1
Line 2
Line 3
Line 4
Line 5'.

" Compare adjacent lines (like Python's itertools.pairwise or Rust's tuple_windows) "
'Pairwise comparison (N-1 iterations for N lines):' printNl.
| pairCount |
pairCount := 0.
LazyFileStream linesWithPreviousDo: 'pairwise.txt' do: [:prev :curr |
    pairCount := pairCount + 1.
    ('  ' , prev , ' -> ' , curr) printNl.
].
('Total pairs: ' , pairCount asString) printNl.
'' printNl.

" Practical use case: detect changes between adjacent lines "
'Detecting changes:' printNl.
File write: 'changes.txt' content: 'stable
stable
CHANGED
stable
stable
CHANGED'.

LazyFileStream linesWithPreviousDo: 'changes.txt' do: [:prev :curr |
    (prev = curr) ifFalse: [
        ('  Change detected: "' , prev , '" -> "' , curr , '"') printNl.
    ].
].
'' printNl.

File delete: 'pairwise.txt'.
File delete: 'changes.txt'.


" ============================================== "
" Cleanup: Remove temporary files               "
" ============================================== "

File delete: 'sample.txt'.
File delete: 'mixed.txt'.
File delete: 'large.txt'.

'=== All streaming examples completed! ===' printNl.

15_argparser.st

"
ArgParser - command-line argument parsing

Build CLI tools with flags, options, and positional arguments.
"

Module import: 'CLI'.  "import: → nil"

| parser result |

"--- Create parser ---"
parser := ArgParser new.  "new → ArgParser"

"--- Set program name ---"
parser name: 'demo-tool'.  "name: → self"

"--- Set description ---"
parser description: 'A demonstration CLI tool'.  "description: → self"

"--- Add boolean flag (--verbose or -v) ---"
parser flag: 'verbose' short: 'v' description: 'Enable verbose output'.  "flag:short:description: → self"

"--- Add option with value (--output FILE or -o FILE) ---"
parser option: 'output' short: 'o' description: 'Output file path'.  "option:short:description: → self"

"--- Add another option ---"
parser option: 'count' short: 'n' description: 'Number of iterations'.

"--- Add positional argument ---"
parser positional: 'input' description: 'Input file to process'.  "positional:description: → self"

"--- Parse command line args ---"
result := parser parse: System args.  "parse: → ParseResult; args → Array"

"--- Check parse success ---"
result isSuccess ifTrue: [  "isSuccess → Boolean"
    '=== Parsed Arguments ===' printNl.

    "--- Get positional value ---"
    'Input: ' print.
    ((result positional: 'input') ifNil: ['(not set)']) printNl.  "positional: → String or nil"

    "--- Get option value ---"
    'Output: ' print.
    ((result option: 'output') ifNil: ['(not set)']) printNl.  "option: → String or nil"

    "--- Get flag value (boolean) ---"
    'Verbose: ' print.
    (result flag: 'verbose') printNl.  "flag: → Boolean"

    'Count: ' print.
    ((result option: 'count') ifNil: ['(not set)']) printNl.

    "--- Get extra arguments ---"
    (result extras size > 0) ifTrue: [  "extras → Array"
        'Extra args: ' print.
        result extras printNl.
    ].
] ifFalse: [
    "--- Handle parse error ---"
    'Error: ' print.
    result errorMessage printNl.  "errorMessage → String"

    "--- Print help ---"
    parser printHelp.  "printHelp → nil"
].

'=== Done! ===' printNl.

16_parallel_process.st

"========================================
 Parallel Process Execution
 DevOps-style parallel command execution
========================================"

"Helper to get shell command based on OS"
| shell shellArg isWindows child exitCode pingCmd pollCount processes codes running maxConcurrency items results echoCmd exitCmd args |

isWindows := System platform = 'windows'.  "platform → String"
shell := isWindows ifTrue: ['cmd'] ifFalse: ['sh'].
shellArg := isWindows ifTrue: ['/c'] ifFalse: ['-c'].

'=== 1. Basic ChildProcess ===' printNl.

"Spawn a process and wait for it"
args := Array new: 2.
args at: 1 put: shellArg.
args at: 2 put: 'echo Hello from child process'.
child := ChildProcess spawn: shell args: args.  "spawn:args: → ChildProcess"
'Spawned process: ' print. child printNl.

"Wait for completion (blocking)"
exitCode := child exitCode.  "exitCode → Integer (blocks until finished)"
'Exit code: ' print. exitCode printNl.
'' printNl.


'=== 2. Non-blocking check ===' printNl.

"Spawn a longer-running process"
pingCmd := isWindows
    ifTrue: ['ping -n 1 127.0.0.1']
    ifFalse: ['ping -c 1 127.0.0.1'].
args := Array new: 2.
args at: 1 put: shellArg.
args at: 2 put: pingCmd.
child := ChildProcess spawn: shell args: args.
'Process spawned, polling...' printNl.

"Poll until finished"
pollCount := 0.
[child isFinished] whileFalse: [  "isFinished → Boolean (non-blocking)"
    pollCount := pollCount + 1.
    (pollCount \\ 50000) = 0 ifTrue: ['..' print].
].
'' printNl.
'Finished after polling ' print. pollCount printNl.
'Exit code: ' print. child exitCode printNl.
'' printNl.


'=== 3. Multiple processes in sequence ===' printNl.

"Run multiple processes and collect results"
processes := OrderedCollection new.
args := Array new: 2. args at: 1 put: shellArg. args at: 2 put: 'echo Job 1'.
processes add: (ChildProcess spawn: shell args: args).
args := Array new: 2. args at: 1 put: shellArg. args at: 2 put: 'echo Job 2'.
processes add: (ChildProcess spawn: shell args: args).
args := Array new: 2. args at: 1 put: shellArg. args at: 2 put: 'echo Job 3'.
processes add: (ChildProcess spawn: shell args: args).

"Wait for all to complete"
codes := processes collect: [:p | p exitCode].  "collect: → Array"
'All jobs completed. Exit codes: ' print. codes printNl.
'' printNl.


'=== 4. Parallel execution with manual pool ===' printNl.

"Run 4 jobs with max 2 concurrent"
maxConcurrency := 2.
running := OrderedCollection new.
results := OrderedCollection new.
items := #('A' 'B' 'C' 'D').

items do: [:item |
    "Wait if at max"
    [running size >= maxConcurrency] whileTrue: [
        running do: [:p |
            p isFinished ifTrue: [
                results add: p exitCode.
            ].
        ].
        running := running select: [:p | p isFinished not].
        1 to: 5000 do: [:i | ].  "brief busy wait"
    ].

    "Spawn new"
    echoCmd := 'echo ', item.
    args := Array new: 2. args at: 1 put: shellArg. args at: 2 put: echoCmd.
    running add: (ChildProcess spawn: shell args: args).
].

"Wait for remaining"
[running isEmpty not] whileTrue: [
    running do: [:p |
        p isFinished ifTrue: [results add: p exitCode].
    ].
    running := running select: [:p | p isFinished not].
    1 to: 5000 do: [:i | ].
].

'Parallel jobs (max 2 concurrent) completed. Exit codes: ' print. results asArray printNl.
'' printNl.


'=== 5. Error handling ===' printNl.

"Process that fails"
exitCmd := isWindows ifTrue: ['exit 42'] ifFalse: ['exit 42'].
args := Array new: 2. args at: 1 put: shellArg. args at: 2 put: exitCmd.
child := ChildProcess spawn: shell args: args.
exitCode := child exitCode.
'Process with exit 42, got: ' print. exitCode printNl.
'' printNl.


'=== All parallel process tests complete! ===' printNl.

17_tableprinter.st

"
TablePrinter Example - East Asian Width Support

Demonstrates TablePrinter with Japanese text.
Full-width characters (Japanese) are properly aligned.
"

'=== TablePrinter Example ===' printNl.
'' printNl.

"Import the Text module"
Module import: 'Text'.  "import: → nil"

"--- Example 1: Basic displayWidth test ---"
'Example 1: displayWidth test' printNl.
('  ''hello'' displayWidth = ', 'hello' displayWidth asString) printNl.  "displayWidth → Integer"
('  ''world'' displayWidth = ', 'world' displayWidth asString) printNl.
('  ''Running'' displayWidth = ', 'Running' displayWidth asString) printNl.
'' printNl.

"--- Example 2: Server status table (English only) ---"
'Example 2: English table' printNl.
| table |
table := TablePrinter new.  "new → TablePrinter"
table headers: #('ID' 'Server Name' 'Status').  "headers: → self"
table addRow: #(1 'web-01' 'Running').  "addRow: → self"
table addRow: #(2 'db-primary' 'Maintenance').
table addRow: #(105 'api-gateway' 'Error').
table printTable.  "printTable → nil (side effect: prints to stdout)"
'' printNl.

"--- Example 3: Mixed table with Japanese ---"
'Example 3: Japanese table' printNl.
| table2 |
table2 := TablePrinter new.
table2 headers: #('ID' 'Server Name' 'Status' 'Memo').
table2 addRow: #(1 'web-01' 'Running' 'OK').
table2 addRow: #(2 'db-primary' 'Maintenance' 'Backup').
table2 addRow: #(105 'api-gateway' 'Error' 'Check').
table2 printTable.
'' printNl.

"--- Example 4: padRightDisplay test ---"
'Example 4: padRightDisplay test' printNl.
| s1 s2 s3 |
s1 := 'ABC'.
s2 := 'XYZ'.
s3 := 'Hello'.
('[', (s1 padRightDisplay: 10), ']') printNl.  "padRightDisplay: → String (padded)"
('[', (s2 padRightDisplay: 10), ']') printNl.
('[', (s3 padRightDisplay: 10), ']') printNl.
'' printNl.

'=== Done ===' printNl.

18_mysql.st

"=== Lambda Smalltalk: MySQL Plugin Example ==="
"Demonstrates MySQL database connection and queries"

"NOTE: This example requires a running MySQL server."
"Adjust the connection string as needed."

"=== Load the MySQL plugin ==="
'Loading MySQL plugin...' printNl.
| pluginName |
pluginName := Plugin load: 'mysql'.  "load: → String (resolves to plugins/mysql_plugin.dll)"
'Loaded: ' print. pluginName printNl.

'Available functions: ' print. (Plugin functions: 'mysql') printNl.  "functions: → Array"

"=== Connect to database ==="
"Connection string format: mysql://user:password@host:port/database"
| db |
db := Plugin call: 'mysql' function: 'connect' args: #('mysql://root:password@localhost:3306/testdb').  "connect → handle or nil"

db ifNil: [
    'Failed to connect to MySQL. Make sure the server is running.' printNl.
] ifNotNil: [
    'Connected! Handle: ' print. db printNl.

    "=== Create a test table ==="
    | result |
    result := Plugin call: 'mysql' function: 'execute' args: { db.
        'CREATE TABLE IF NOT EXISTS users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            name VARCHAR(100) NOT NULL,
            age INT,
            email VARCHAR(100)
        )' }.
    'Create table: ' print. result printNl.

    "=== Insert data with parameters ==="
    result := Plugin call: 'mysql' function: 'execute_params'
        args: { db. 'INSERT INTO users (name, age, email) VALUES (?, ?, ?)'. #('Alice' 30 'alice@example.com') }.
    'Insert Alice: ' print. result printNl.

    result := Plugin call: 'mysql' function: 'execute_params'
        args: { db. 'INSERT INTO users (name, age, email) VALUES (?, ?, ?)'. #('Bob' 25 'bob@example.com') }.
    'Insert Bob: ' print. result printNl.

    result := Plugin call: 'mysql' function: 'execute_params'
        args: { db. 'INSERT INTO users (name, age, email) VALUES (?, ?, ?)'. #('Charlie' 35 'charlie@example.com') }.
    'Insert Charlie: ' print. result printNl.

    "=== Query all users ==="
    'All users:' printNl.
    | rows |
    rows := Plugin call: 'mysql' function: 'query' args: { db. 'SELECT * FROM users' }.  "query → Array of Dicts"
    rows do: [:row |
        '  ' print. row printNl.
    ].

    "=== Query with parameters ==="
    'Users older than 28:' printNl.
    rows := Plugin call: 'mysql' function: 'query_params'
        args: { db. 'SELECT name, age FROM users WHERE age > ?'. #(28) }.
    rows do: [:row |
        '  ' print. (row at: 'name') print. ' is ' print. (row at: 'age') print. ' years old' printNl.
    ].

    "=== Update data ==="
    result := Plugin call: 'mysql' function: 'execute_params'
        args: { db. 'UPDATE users SET age = ? WHERE name = ?'. #(31 'Alice') }.
    'Update Alice age: ' print. result printNl.

    "=== Delete data ==="
    result := Plugin call: 'mysql' function: 'execute_params'
        args: { db. 'DELETE FROM users WHERE name = ?'. #('Charlie') }.
    'Delete Charlie: ' print. result printNl.

    "=== Final state ==="
    'Final users:' printNl.
    rows := Plugin call: 'mysql' function: 'query' args: { db. 'SELECT * FROM users' }.
    rows do: [:row |
        '  ' print. row printNl.
    ].

    "=== Cleanup ==="
    result := Plugin call: 'mysql' function: 'execute' args: { db. 'DROP TABLE users' }.
    'Drop table: ' print. result printNl.

    "=== Close connection ==="
    result := Plugin call: 'mysql' function: 'close' args: { db }.  "close → true"
    'Connection closed: ' print. result printNl.
].

'=== Done! ===' printNl.

19_tcp.st

"
Lambda Smalltalk - TCP Networking Example
Demonstrates TCP client and server operations.
"

'=== TCP Client Example ===' printNl.

"Note: This example requires an actual server to connect to.
 For testing, you can use: nc -l 12345 (netcat) or similar.

 Basic usage:
   sock := Tcp connect: 'localhost' port: 12345.
   sock send: 'Hello, server!'.
   response := sock recvLine.
   response printNl.
   sock close.
"

'--- TCP API Overview ---' printNl.
'  Tcp connect: host port: port         - → TcpStream or nil' printNl.
'  Tcp connect: host port: port timeout: ms  - → TcpStream or nil' printNl.
'  stream send: data                    - → Integer (bytes sent)' printNl.
'  stream recv: maxBytes                - → ByteArray or nil' printNl.
'  stream recv                          - → ByteArray or nil' printNl.
'  stream recvLine                      - → String or nil' printNl.
'  stream close                         - → true' printNl.
'' printNl.
'  TcpListener listen: port             - → TcpListener or nil' printNl.
'  listener accept                      - → TcpStream (blocks)' printNl.
'  listener close                       - → true' printNl.
'' printNl.

"
=== Simple HTTP Client Example ===
This demonstrates making a raw HTTP request over TCP.
"

'--- Minimal HTTP Client ---' printNl.
"
sock := Tcp connect: 'httpbin.org' port: 80.
sock ifNotNil: [
    sock send: 'GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n'.

    response := sock recv: 4096.
    response ifNotNil: [
        (response asString) printNl.
    ].
    sock close.
    'HTTP request complete.' printNl.
] ifNil: [
    'Connection failed.' printNl.
].
"

'--- TCP Server Example (pseudocode) ---' printNl.
"
server := TcpListener listen: 8080.
server ifNotNil: [
    'Listening on port 8080...' printNl.

    client := server accept.
    client ifNotNil: [
        request := client recvLine.
        'Received: ' print. request printNl.

        client send: 'Hello from Lambda Smalltalk!'.
        client close.
    ].

    server close.
].
"

'=== Redis Client Example (pseudocode) ===' printNl.
"
Redis uses a simple text protocol (RESP).
With TCP support, you can build a Redis client in pure Smalltalk.
"

"
Object subclass: #Redis instanceVariableNames: 'sock'.

Redis class >> connect: host port: port
    | r |
    r := self new.
    r sock: (Tcp connect: host port: port).
    ^ r
!

Redis >> ping
    sock send: '*1\r\n$4\r\nPING\r\n'.
    ^ sock recvLine  ''Should return +PONG''
!

Redis >> set: key value: value
    | keyLen valLen |
    keyLen := key size.
    valLen := value size.
    sock send: '*3\r\n$3\r\nSET\r\n$', keyLen asString, '\r\n', key, '\r\n$', valLen asString, '\r\n', value, '\r\n'.
    ^ sock recvLine
!

Redis >> get: key
    | keyLen |
    keyLen := key size.
    sock send: '*2\r\n$3\r\nGET\r\n$', keyLen asString, '\r\n', key, '\r\n'.
    ^ sock recvLine
!

''Usage:''
redis := Redis connect: 'localhost' port: 6379.
redis ping printNl.                    ''-> +PONG''
redis set: 'mykey' value: 'myvalue'.
redis get: 'mykey' printNl.            ''-> myvalue''
"

'TCP examples loaded.' printNl.

20_excel.st

"
Lambda Smalltalk - Excel Plugin Example
Demonstrates: Plugin loading, Excel read/write, multi-sheet support

Requires: excel_plugin (built automatically)
"

Smalltalk fileIn: 'examples/lib/excel.st'.  "fileIn: → nil (loads library)"

| excel rows sheets testFile |
testFile := '_test_excel_example.xlsx'.

"Create Excel file with multiple sheets"
'--- Creating Excel File ---' printNl.
excel := Excel load.  "load → Excel instance"
excel newWorkbook.  "newWorkbook → self"

"Sheet 1: Report"
excel addWorksheet: 'Report'.  "addWorksheet: → self"
excel writeString: 'Date' row: 0 col: 0 sheet: 'Report'.  "writeString:row:col:sheet: → self"
excel writeString: 'Value' row: 0 col: 1 sheet: 'Report'.
excel writeString: '2026-01-10' row: 1 col: 0 sheet: 'Report'.
excel writeNumber: 123.45 row: 1 col: 1 sheet: 'Report'.  "writeNumber:row:col:sheet: → self"
excel writeString: '2026-01-11' row: 2 col: 0 sheet: 'Report'.
excel writeNumber: 678.90 row: 2 col: 1 sheet: 'Report'.

"Sheet 2: Summary"
excel addWorksheet: 'Summary'.
excel writeString: 'Total' row: 0 col: 0 sheet: 'Summary'.
excel writeNumber: 802.35 row: 0 col: 1 sheet: 'Summary'.

('Saving to ' , testFile , '...') printNl.
excel save: testFile.  "save: → true"

"Read back: sheet names"
'--- Reading Sheet Names ---' printNl.
excel := Excel load.
sheets := excel sheetNames: testFile.  "sheetNames: → Array of Strings"
'Sheets: ' print. sheets printNl.

"Read back: specific sheet"
'--- Reading Report Sheet ---' printNl.
rows := excel readSheet: 'Report' from: testFile.  "readSheet:from: → Array of rows"
rows do: [:row |  "do: → nil"
    row do: [:cell | cell print. '	' print].
    '' printNl.
].

"Read back: using read: (first sheet as dicts)"
'--- Reading as Dicts ---' printNl.
rows := excel read: testFile.  "read: → Array of Dicts (header row becomes keys)"
rows do: [:row |
    'Date: ' print. (row at: 'Date') print.  "at: → cell value"
    ', Value: ' print. (row at: 'Value') printNl.
].

"Cleanup"
File delete: testFile.  "delete: → true (error on failure)"
('Cleaned up: ' , testFile) printNl.

'Done.' printNl.

21_postgres.st

"
Lambda Smalltalk - PostgreSQL Plugin Example
Requires: postgres_plugin.dll and a running PostgreSQL server

NOTE: This example requires a PostgreSQL server.
If you don't have one, install PostgreSQL or skip this example.

Connection string format:
  'host=localhost user=postgres password=yourpass dbname=testdb'
"

| db rows |

'--- Loading PostgreSQL Library ---' printNl.
Smalltalk fileIn: 'examples/lib/postgres.st'.  "fileIn: → nil (loads library)"

'--- Connecting to PostgreSQL ---' printNl.
"Change this connection string to match your PostgreSQL setup"
db := Postgres connect: 'host=localhost user=postgres password=postgres dbname=postgres'.  "connect: → connection or nil"
db = nil ifTrue: [
    'ERROR: Could not connect to PostgreSQL' printNl.
    'Make sure PostgreSQL is running and connection string is correct' printNl.
    ^ self
].
'Connected successfully' printNl.

'--- Creating Test Table ---' printNl.
(db execute: 'DROP TABLE IF EXISTS test_users') ifFalse: [  "execute: → Boolean"
    'Failed to drop table' printNl. ^ self
].
(db execute: 'CREATE TABLE test_users (id SERIAL PRIMARY KEY, name VARCHAR(100), age INTEGER)') ifFalse: [
    'Failed to create table' printNl. ^ self
].
'Table created' printNl.

'--- Inserting Data ---' printNl.
(db execute: 'INSERT INTO test_users (name, age) VALUES (''Alice'', 30)') ifFalse: [
    'Failed to insert Alice' printNl. ^ self
].
(db execute: 'INSERT INTO test_users (name, age) VALUES (''Bob'', 25)') ifFalse: [
    'Failed to insert Bob' printNl. ^ self
].
'Data inserted' printNl.

'--- Inserting with Parameters ---' printNl.
(db execute: 'INSERT INTO test_users (name, age) VALUES ($1, $2)' params: #('Charlie' 35)) ifFalse: [  "execute:params: → Boolean"
    'Failed to insert Charlie' printNl. ^ self
].
'Parameterized insert successful' printNl.

'--- Querying All Rows ---' printNl.
rows := db query: 'SELECT id, name, age FROM test_users ORDER BY id'.  "query: → Array of Dicts or nil"
rows = nil ifTrue: [
    'Query failed' printNl. ^ self
].

'Query results:' printNl.
rows do: [:row |  "do: → nil"
    "PostgreSQL plugin now returns proper Dicts!"
    'ID: ' print. (row at: 'id') print.  "at: → column value"
    ', Name: ' print. (row at: 'name') print.
    ', Age: ' print. (row at: 'age') printNl.
].

'--- Querying with Parameters ---' printNl.
rows := db queryWith: 'SELECT name, age FROM test_users WHERE age > $1' params: #(28).  "queryWith:params: → Array of Dicts or nil"
rows = nil ifTrue: [
    'Parameterized query failed' printNl. ^ self
].

'Users older than 28:' printNl.
rows do: [:row |
    (row at: 'name') print. ' (age: ' print. (row at: 'age') print. ')' printNl.
].

'--- Cleanup ---' printNl.
(db execute: 'DROP TABLE test_users') ifFalse: [
    'Failed to drop table' printNl. ^ self
].
'Table dropped' printNl.

'--- PostgreSQL Plugin Test Complete ---' printNl.

22_template.st

"============================================
 19_template.st - Mustache Template Demo
 ============================================
 Demonstrates: Template rendering with data substitution
"

'=== Basic Template ===' printNl.

"Simple variable substitution"
| tmpl data result |
tmpl := 'Hello, {{name}}!'.
data := Dict new.  "new → empty Dict"
data at: 'name' put: 'Alice'.  "at:put: → value"
result := Template render: tmpl with: data.  "render:with: → String"
result printNl.


'=== Template with Multiple Variables ===' printNl.

"Multiple variables"
tmpl := 'Name: {{name}}, Age: {{age}}, City: {{city}}'.
data := Dict new.
data at: 'name' put: 'Bob'.
data at: 'age' put: 30.
data at: 'city' put: 'Tokyo'.
result := Template render: tmpl with: data.  "render:with: → String (substituted)"
result printNl.


'=== Template with Lists ===' printNl.

"Iterate over array items - {{#section}}...{{/section}} loops over array"
tmpl := 'Items:
{{#items}}
  - {{name}}: ${{price}}
{{/items}}'.
data := Dict new.
| items |
items := Array new: 3.  "new: → Array of given size"
| item1 item2 item3 |
item1 := Dict new.
item1 at: 'name' put: 'Apple'.
item1 at: 'price' put: 100.
item2 := Dict new.
item2 at: 'name' put: 'Banana'.
item2 at: 'price' put: 80.
item3 := Dict new.
item3 at: 'name' put: 'Orange'.
item3 at: 'price' put: 120.
items at: 1 put: item1.  "at:put: → item"
items at: 2 put: item2.
items at: 3 put: item3.
data at: 'items' put: items.
result := Template render: tmpl with: data.  "render:with: → String (list expanded)"
result printNl.


'=== Template with Conditionals ===' printNl.

"Show/hide sections based on data - {{#key}} shows if true, {{^key}} shows if false"
tmpl := 'Hello{{#premium}}, Premium User{{/premium}}{{^premium}}, Guest{{/premium}}!'.
| data1 data2 |
data1 := Dict new.
data1 at: 'premium' put: true.
('Premium user: ' , (Template render: tmpl with: data1)) printNl.  "render:with: → String"

data2 := Dict new.
data2 at: 'premium' put: false.
('Regular user: ' , (Template render: tmpl with: data2)) printNl.


'=== HTML Template ===' printNl.

"Generate HTML report - combining variables and lists"
tmpl := '<html>
<head><title>{{title}}</title></head>
<body>
  <h1>{{title}}</h1>
  <ul>
  {{#users}}
    <li>{{name}} - {{email}}</li>
  {{/users}}
  </ul>
</body>
</html>'.
data := Dict new.
data at: 'title' put: 'User List'.
| users |
users := Array new: 2.
| user1 user2 |
user1 := Dict new.
user1 at: 'name' put: 'Alice'.
user1 at: 'email' put: 'alice@example.com'.
user2 := Dict new.
user2 at: 'name' put: 'Bob'.
user2 at: 'email' put: 'bob@example.com'.
users at: 1 put: user1.
users at: 2 put: user2.
data at: 'users' put: users.
result := Template render: tmpl with: data.  "render:with: → String (HTML output)"
result printNl.


'=== Done! ===' printNl.

23_redis.st

"=== Lambda Smalltalk: Redis Plugin ==="
"Redis client with global connection and handle-based connection pool"

"=== Setup ==="
"Build the plugin:"
"  cd examples/plugins/redis_plugin && cargo build --release"
"Copy to plugins/:"
"  cp target/release/redis_plugin.dll ../../plugins/"
"Requires Redis server running on localhost:6379"

'Loading redis plugin...' printNl.
Plugin load: 'redis'.

"=== Global Connection (Simple) ==="
'--- Global Connection ---' printNl.
| connected |
connected := Plugin call: 'redis' function: 'connect' args: #('redis://localhost:6379').
connected ifNil: [
    'Failed to connect to Redis. Make sure the server is running on localhost:6379.' printNl.
    '=== Done! ===' printNl.
    ^ nil
].
'Connected to Redis' printNl.

"Basic operations"
'--- String Operations ---' printNl.
Plugin call: 'redis' function: 'set' args: #('greeting' 'Hello from Lambda Smalltalk!').
| value |
value := Plugin call: 'redis' function: 'get' args: #('greeting').
('GET greeting: ' , value) printNl.

"Check existence"
| exists |
exists := Plugin call: 'redis' function: 'exists' args: #('greeting').
('EXISTS greeting: ' , exists asString) printNl.

"Delete"
| deleted |
deleted := Plugin call: 'redis' function: 'del' args: #('greeting').
('DEL greeting (deleted count): ' , deleted asString) printNl.

'--- Counter Operations ---' printNl.
Plugin call: 'redis' function: 'set' args: #('counter' '0').
| count1 count2 count3 |
count1 := Plugin call: 'redis' function: 'incr' args: #('counter').
count2 := Plugin call: 'redis' function: 'incr' args: #('counter').
count3 := Plugin call: 'redis' function: 'incr' args: #('counter').
('INCR counter: ' , count1 asString , ' -> ' , count2 asString , ' -> ' , count3 asString) printNl.

'--- TTL Operations ---' printNl.
Plugin call: 'redis' function: 'set' args: #('temp_key' 'expires soon').
Plugin call: 'redis' function: 'expire' args: #('temp_key' 60).
| ttl |
ttl := Plugin call: 'redis' function: 'ttl' args: #('temp_key').
('TTL temp_key: ' , ttl asString , ' seconds') printNl.

'--- List Operations ---' printNl.
Plugin call: 'redis' function: 'del' args: #('mylist').
Plugin call: 'redis' function: 'rpush' args: #('mylist' 'first').
Plugin call: 'redis' function: 'rpush' args: #('mylist' 'second').
Plugin call: 'redis' function: 'lpush' args: #('mylist' 'zeroth').
| list |
list := Plugin call: 'redis' function: 'lrange' args: #('mylist' 0 -1).
('LRANGE mylist 0 -1: ' , list asString) printNl.

'--- Hash Operations ---' printNl.
Plugin call: 'redis' function: 'hset' args: #('user:1' 'name' 'Alice').
Plugin call: 'redis' function: 'hset' args: #('user:1' 'email' 'alice@example.com').
Plugin call: 'redis' function: 'hset' args: #('user:1' 'age' '30').
| name email allFields |
name := Plugin call: 'redis' function: 'hget' args: #('user:1' 'name').
email := Plugin call: 'redis' function: 'hget' args: #('user:1' 'email').
('HGET user:1 name: ' , name) printNl.
('HGET user:1 email: ' , email) printNl.
allFields := Plugin call: 'redis' function: 'hgetall' args: #('user:1').
('HGETALL user:1: ' , allFields asString) printNl.

'--- Keys Pattern ---' printNl.
| keys |
keys := Plugin call: 'redis' function: 'keys' args: #('user:*').
('KEYS user:*: ' , keys asString) printNl.

"Cleanup global test data"
Plugin call: 'redis' function: 'del' args: #('counter').
Plugin call: 'redis' function: 'del' args: #('temp_key').
Plugin call: 'redis' function: 'del' args: #('mylist').
Plugin call: 'redis' function: 'del' args: #('user:1').

"Disconnect global connection"
Plugin call: 'redis' function: 'disconnect' args: #().
'Disconnected from Redis (global)' printNl.

"=== Handle-based Connections (Multiple) ==="
'--- Handle-based Connection Pool ---' printNl.
| conn1 conn2 |

"Open multiple connections"
conn1 := Plugin call: 'redis' function: 'open' args: #('redis://localhost:6379').
conn2 := Plugin call: 'redis' function: 'open' args: #('redis://localhost:6379').
('Opened connections: ' , conn1 asString , ', ' , conn2 asString) printNl.

"Use different connections"
Plugin call: 'redis' function: 'set_on' args: { conn1. 'conn1_key'. 'value from conn1' }.
Plugin call: 'redis' function: 'set_on' args: { conn2. 'conn2_key'. 'value from conn2' }.

| val1 val2 |
val1 := Plugin call: 'redis' function: 'get_on' args: { conn1. 'conn1_key' }.
val2 := Plugin call: 'redis' function: 'get_on' args: { conn2. 'conn2_key' }.
('Connection 1 read: ' , val1) printNl.
('Connection 2 read: ' , val2) printNl.

"Cross-read (data is shared in Redis)"
| cross1 cross2 |
cross1 := Plugin call: 'redis' function: 'get_on' args: { conn1. 'conn2_key' }.
cross2 := Plugin call: 'redis' function: 'get_on' args: { conn2. 'conn1_key' }.
('Conn1 reads conn2_key: ' , cross1) printNl.
('Conn2 reads conn1_key: ' , cross2) printNl.

"Cleanup"
Plugin call: 'redis' function: 'set_on' args: { conn1. 'conn1_key'. '' }.
Plugin call: 'redis' function: 'set_on' args: { conn1. 'conn2_key'. '' }.
Plugin call: 'redis' function: 'close' args: { conn1 }.
Plugin call: 'redis' function: 'close' args: { conn2 }.
'Closed both connections' printNl.

'=== Done! ===' printNl.

24_sysinfo.st

"=== Lambda Smalltalk: SysInfo Plugin ==="
"System monitoring: CPU, memory, processes, disk, network"

"=== Setup ==="
"Build the plugin:"
"  cd examples/plugins/sysinfo_plugin && cargo build --release"
"Copy to plugins/:"
"  cp target/release/sysinfo_plugin.dll ../../plugins/"

'Loading sysinfo plugin...' printNl.
Plugin load: 'sysinfo'.

'=== System Information ===' printNl.
| hostName osVersion kernelVersion uptime |
hostName := Plugin call: 'sysinfo' function: 'host_name' args: #().
osVersion := Plugin call: 'sysinfo' function: 'os_version' args: #().
kernelVersion := Plugin call: 'sysinfo' function: 'kernel_version' args: #().
uptime := Plugin call: 'sysinfo' function: 'uptime' args: #().

('Host: ' , hostName) printNl.
('OS: ' , osVersion) printNl.
('Kernel: ' , kernelVersion) printNl.
('Uptime: ' , uptime asString , ' seconds') printNl.

'=== Memory ===' printNl.
| totalMem usedMem availMem totalSwap usedSwap |
totalMem := Plugin call: 'sysinfo' function: 'total_memory' args: #().
usedMem := Plugin call: 'sysinfo' function: 'used_memory' args: #().
availMem := Plugin call: 'sysinfo' function: 'available_memory' args: #().
totalSwap := Plugin call: 'sysinfo' function: 'total_swap' args: #().
usedSwap := Plugin call: 'sysinfo' function: 'used_swap' args: #().

('Total Memory: ' , (totalMem / 1048576.0) truncated asString , ' MB') printNl.
('Used Memory: ' , (usedMem / 1048576.0) truncated asString , ' MB') printNl.
('Available: ' , (availMem / 1048576.0) truncated asString , ' MB') printNl.
('Total Swap: ' , (totalSwap / 1048576.0) truncated asString , ' MB') printNl.
('Used Swap: ' , (usedSwap / 1048576.0) truncated asString , ' MB') printNl.

'=== CPU ===' printNl.
| cpuUsage cpuCount physCores |
cpuUsage := Plugin call: 'sysinfo' function: 'cpu_usage' args: #().
cpuCount := Plugin call: 'sysinfo' function: 'cpu_count' args: #().
physCores := Plugin call: 'sysinfo' function: 'physical_core_count' args: #().

('CPU Usage: ' , cpuUsage asString , '%') printNl.
('Logical CPUs: ' , cpuCount asString) printNl.
('Physical Cores: ' , physCores asString) printNl.

'=== Load Average ===' printNl.
| loadAvg |
loadAvg := Plugin call: 'sysinfo' function: 'load_average' args: #().
('Load Average (1/5/15 min): ' , (loadAvg at: 1) asString , ' / ' , (loadAvg at: 2) asString , ' / ' , (loadAvg at: 3) asString) printNl.

'=== Top 5 Processes by CPU ===' printNl.
| topCpu |
topCpu := Plugin call: 'sysinfo' function: 'top_processes_cpu' args: #(5).
topCpu do: [:proc |
    | pid name cpu mem |
    pid := proc at: 1.
    name := proc at: 2.
    cpu := proc at: 3.
    mem := ((proc at: 4) / 1048576.0) truncated.
    ('  PID ' , pid asString , ': ' , name , ' - CPU: ' , cpu asString , '%, Mem: ' , mem asString , ' MB') printNl.
].

'=== Top 5 Processes by Memory ===' printNl.
| topMem |
topMem := Plugin call: 'sysinfo' function: 'top_processes_mem' args: #(5).
topMem do: [:proc |
    | pid name cpu mem |
    pid := proc at: 1.
    name := proc at: 2.
    cpu := proc at: 3.
    mem := ((proc at: 4) / 1048576.0) truncated.
    ('  PID ' , pid asString , ': ' , name , ' - CPU: ' , cpu asString , '%, Mem: ' , mem asString , ' MB') printNl.
].

'=== Disk Information ===' printNl.
| disks |
disks := Plugin call: 'sysinfo' function: 'disk_info' args: #().
disks do: [:disk |
    | name mount total avail fs |
    name := disk at: 1.
    mount := disk at: 2.
    total := ((disk at: 3) / 1073741824.0) truncated.
    avail := ((disk at: 4) / 1073741824.0) truncated.
    fs := disk at: 5.
    ('  ' , mount , ' (' , name , ', ' , fs , '): ' , avail asString , ' / ' , total asString , ' GB free') printNl.
].

'=== Network Interfaces ===' printNl.
| networks |
networks := Plugin call: 'sysinfo' function: 'network_info' args: #().
networks do: [:net |
    | name rx tx |
    name := net at: 1.
    rx := (net at: 2) // 1048576.
    tx := (net at: 3) // 1048576.
    ('  ' , name , ': RX ' , rx asString , ' MB, TX ' , tx asString , ' MB') printNl.
].

'=== Done! ===' printNl.

25_fastcgi.st

"
Lambda Smalltalk - FastCGI Example
Demonstrates the FastCGI protocol library for web server integration.

To test with nginx:
1. Run this script: lambda-st run examples/25_fastcgi.st
2. Configure nginx with:
   location /app {
       fastcgi_pass 127.0.0.1:9000;
       include fastcgi_params;
   }
3. Access http://localhost/app?name=World

Note: This example requires lib/FastCGI/FastCGI.st
"

'=== FastCGI Protocol Example ===' printNl.
'' printNl.

"Load FastCGI library"
Smalltalk fileIn: 'lib/FastCGI/FastCGI.st'.

'=== 1. FCGI Protocol Constants ===' printNl.
'FCGI Record Types:' printNl.
('  BEGIN_REQUEST = ', FCGI BEGIN_REQUEST asString) printNl.
('  PARAMS        = ', FCGI PARAMS asString) printNl.
('  STDIN         = ', FCGI STDIN asString) printNl.
('  STDOUT        = ', FCGI STDOUT asString) printNl.
('  END_REQUEST   = ', FCGI END_REQUEST asString) printNl.
'' printNl.

'=== 2. Parse FastCGI Name-Value Pairs ===' printNl.
'Parsing FCGI_PARAMS format:' printNl.

| content params |

"Build a test PARAMS payload: REQUEST_METHOD=GET"
"Format: nameLen(1) + valueLen(1) + name + value"
content := ByteArray new: 17.
"REQUEST_METHOD (14 chars) = GET (3 chars)"
content at: 1 put: 14.  "nameLen"
content at: 2 put: 3.   "valueLen"
"REQUEST_METHOD"
content at: 3 put: $R asciiValue.
content at: 4 put: $E asciiValue.
content at: 5 put: $Q asciiValue.
content at: 6 put: $U asciiValue.
content at: 7 put: $E asciiValue.
content at: 8 put: $S asciiValue.
content at: 9 put: $T asciiValue.
content at: 10 put: $_ asciiValue.
content at: 11 put: $M asciiValue.
content at: 12 put: $E asciiValue.
content at: 13 put: $T asciiValue.
content at: 14 put: $H asciiValue.
content at: 15 put: $O asciiValue.
content at: 16 put: $D asciiValue.
"GET"
content at: 17 put: $G asciiValue.
content at: 18 put: $E asciiValue.
content at: 19 put: $T asciiValue.

"Need to resize ByteArray - recreate with correct size"
content := ByteArray new: 19.
content at: 1 put: 14.
content at: 2 put: 3.
content at: 3 put: 82.   "R"
content at: 4 put: 69.   "E"
content at: 5 put: 81.   "Q"
content at: 6 put: 85.   "U"
content at: 7 put: 69.   "E"
content at: 8 put: 83.   "S"
content at: 9 put: 84.   "T"
content at: 10 put: 95.  "_"
content at: 11 put: 77.  "M"
content at: 12 put: 69.  "E"
content at: 13 put: 84.  "T"
content at: 14 put: 72.  "H"
content at: 15 put: 79.  "O"
content at: 16 put: 68.  "D"
content at: 17 put: 71.  "G"
content at: 18 put: 69.  "E"
content at: 19 put: 84.  "T"

params := FastCGI parseParams: content.
('  Parsed params: ', params asString) printNl.
('  REQUEST_METHOD = ', (params at: 'REQUEST_METHOD')) printNl.
'' printNl.

'=== 3. Build FCGIRequest ===' printNl.
'Creating FCGIRequest:' printNl.

| request |
request := FCGIRequest new.
request params at: 'REQUEST_METHOD' put: 'POST'.
request params at: 'QUERY_STRING' put: 'name=Alice&age=30'.
request params at: 'SCRIPT_NAME' put: '/app'.
request params at: 'PATH_INFO' put: '/users'.
request params at: 'CONTENT_TYPE' put: 'application/json'.
request params at: 'CONTENT_LENGTH' put: '42'.
request params at: 'SERVER_NAME' put: 'localhost'.
request params at: 'SERVER_PORT' put: '80'.
request params at: 'REMOTE_ADDR' put: '127.0.0.1'.
request stdin: '{"username": "alice", "email": "alice@example.com"}'.

('  Request method: ', request requestMethod) printNl.
('  Script name: ', request scriptName) printNl.
('  Path info: ', request pathInfo) printNl.
('  Query string: ', request queryString) printNl.
('  Content type: ', request contentType) printNl.
('  Content length: ', request contentLength) printNl.
('  Server: ', request serverName, ':', request serverPort) printNl.
('  Remote addr: ', request remoteAddr) printNl.
('  Body: ', request stdin) printNl.
'' printNl.

'=== 4. Parse Query Parameters ===' printNl.
'Parsing query string:' printNl.

| qp |
qp := request queryParams.
('  Query params: ', qp asString) printNl.
('  name = ', (qp at: 'name')) printNl.
('  age = ', (qp at: 'age')) printNl.
'' printNl.

'=== 5. Build END_REQUEST Record ===' printNl.
'Building END_REQUEST record:' printNl.

| endRecord |
endRecord := FastCGI endRequestRecord: 1 status: 0.
('  Record type: ', endRecord type asString, ' (END_REQUEST = 3)') printNl.
('  Request ID: ', endRecord requestId asString) printNl.
('  Content size: ', endRecord content size asString, ' bytes') printNl.
'' printNl.

'=== 6. Simple Request Handler Example ===' printNl.
'Example request handler:' printNl.

| handler response |
handler := [:req |
    | name greeting |
    name := (req queryParams at: 'name' ifAbsent: ['World']).
    greeting := 'Hello, ', name, '!'.
    'Content-Type: text/html\r\n\r\n<html><body><h1>', greeting, '</h1></body></html>'
].

response := handler value: request.
'  Handler response:' printNl.
('    ', response) printNl.
'' printNl.

'=== 7. Start FastCGI Server (commented out) ===' printNl.
'To start a FastCGI server, use:' printNl.
'  FastCGI run: [:request |' printNl.
'      | name |' printNl.
'      name := request queryParams at: ''name'' ifAbsent: [''World''].' printNl.
'      ''Content-Type: text/html\r\n\r\nHello, '', name, ''!''' printNl.
'  ].' printNl.
'' printNl.

"Uncomment below to actually start the server:"
"
FastCGI run: [:request |
    | name |
    name := request queryParams at: 'name' ifAbsent: ['World'].
    'Content-Type: text/html\r\n\r\nHello, ', name, '!'
].
"

'=== Done! ===' printNl.

26_signal.st

"=== Signal Handling ==="
"Lambda Smalltalk supports POSIX-style signal handling for graceful shutdown."

'=== Signal Handling Demo ===' printNl.

"--- Basic Usage ---"
"Register a handler for Ctrl+C (SIGINT)"

| running counter |
running := true.
counter := 0.

"Register interrupt handler"
System onInterrupt: [
    '
*** Ctrl+C received! Shutting down gracefully... ***' printNl.
    running := false
].

'Press Ctrl+C to interrupt the loop (auto-exits after 5 iterations for demo)' printNl.
'Handler registered.' printNl.
'' printNl.

"Simulate a long-running task"
[running and: [counter < 5]] whileTrue: [
    counter := counter + 1.
    ('Working... iteration ', counter asString) printNl.

    "Simulate work with a simple busy loop"
    | i |
    i := 0.
    [i < 1000000] whileTrue: [ i := i + 1 ].
].

running
    ifTrue: [ 'Completed normally after 5 iterations.' printNl ]
    ifFalse: [ 'Interrupted by user!' printNl ].

"Clean up - clear the handler"
System clearInterruptHandler.
'Handler cleared.' printNl.

"--- Common Patterns ---"
'' printNl.
'=== Common Patterns ===' printNl.

"Pattern 1: Exit with standard SIGINT exit code (128 + signal number)"
'1. Exit with SIGINT code:' printNl.
'   System onInterrupt: [ System exit: 130 ].' printNl.
'' printNl.

"Pattern 2: Resource cleanup before exit"
'2. Cleanup before exit:' printNl.
'   System onInterrupt: [' printNl.
'       file close.' printNl.
'       db disconnect.' printNl.
'       System exit: 130' printNl.
'   ].' printNl.
'' printNl.

"Pattern 3: Graceful flag-based shutdown (shown above)"
'3. Flag-based shutdown:' printNl.
'   running := true.' printNl.
'   System onInterrupt: [ running := false ].' printNl.
'   [ running ] whileTrue: [ doWork ].' printNl.
'' printNl.

"Pattern 4: Counter for double Ctrl+C confirmation"
'4. Double Ctrl+C to force quit:' printNl.
'   | interruptCount |' printNl.
'   interruptCount := 0.' printNl.
'   System onInterrupt: [' printNl.
'       interruptCount := interruptCount + 1.' printNl.
'       interruptCount = 1 ifTrue: [ ''Press Ctrl+C again to quit'' printNl ].' printNl.
'       interruptCount >= 2 ifTrue: [ System exit: 130 ].' printNl.
'   ].' printNl.

'Done!' printNl.

27_dataframe.st

"=== Lambda Smalltalk: DataFrame Plugin (Polars) ==="
"High-performance DataFrame operations using Rust's Polars library"
"Features: parallel CSV parsing, lazy evaluation, automatic parallelization"

"=== Setup ==="
"First, build the plugin:"
"  cd examples/plugins/dataframe_plugin && cargo build --release"
"Then copy the DLL to plugins/:"
"  cp target/release/dataframe_plugin.dll ../../plugins/"

'Loading dataframe plugin...' printNl.
Plugin load: 'dataframe'.

"=== Create test CSV ==="
| csvPath df filteredDf groupedDf sortedDf selectedDf |
csvPath := 'examples/temp_sales.csv'.

"Write sample CSV data"
(File write: csvPath content: 'id,category,region,amount,quantity
1,Electronics,North,599.99,2
2,Clothing,South,49.99,5
3,Electronics,East,899.99,1
4,Food,West,25.50,10
5,Books,North,19.99,3
6,Sports,South,149.99,2
7,Clothing,East,79.99,4
8,Electronics,West,1299.99,1
9,Food,North,35.00,8
10,Books,South,29.99,2').

"=== Read CSV ==="
'--- Reading CSV ---' printNl.
df := Plugin call: 'dataframe' function: 'read_csv' args: { csvPath }.
('Loaded DataFrame: ' , (Plugin call: 'dataframe' function: 'describe' args: { df })) printNl.

"=== Get shape and columns ==="
'--- Shape and columns ---' printNl.
('Shape: ' , (Plugin call: 'dataframe' function: 'shape' args: { df }) asString) printNl.
('Columns: ' , (Plugin call: 'dataframe' function: 'columns' args: { df }) asString) printNl.

"=== Select specific columns ==="
'--- Select columns ---' printNl.
selectedDf := Plugin call: 'dataframe' function: 'select' args: { df. #('category' 'amount') }.
(Plugin call: 'dataframe' function: 'to_json' args: { selectedDf }) printNl.

"=== Filter rows ==="
'--- Filter (amount > 100) ---' printNl.
filteredDf := Plugin call: 'dataframe' function: 'filter' args: { df. 'amount'. '>'. 100 }.
(Plugin call: 'dataframe' function: 'to_json' args: { filteredDf }) printNl.

"=== Group by + aggregation ==="
'--- Group by category, sum amount ---' printNl.
groupedDf := Plugin call: 'dataframe' function: 'group_sum' args: { df. 'category'. 'amount' }.
(Plugin call: 'dataframe' function: 'to_json' args: { groupedDf }) printNl.

"=== Sort ==="
'--- Sort by amount (descending) ---' printNl.
sortedDf := Plugin call: 'dataframe' function: 'sort' args: { df. 'amount'. true }.
(Plugin call: 'dataframe' function: 'to_json' args: { sortedDf }) printNl.

"=== Cleanup ==="
Plugin call: 'dataframe' function: 'drop' args: { df }.
Plugin call: 'dataframe' function: 'drop' args: { filteredDf }.
Plugin call: 'dataframe' function: 'drop' args: { groupedDf }.
Plugin call: 'dataframe' function: 'drop' args: { sortedDf }.
Plugin call: 'dataframe' function: 'drop' args: { selectedDf }.
File delete: csvPath.

'=== Done! ===' printNl.

28_router.st

"
16_router.st - Sinatra-like Web Router Demo

This example demonstrates the WebApp router for building REST APIs.
To run the server: lambda-st run examples/16_router.st
Then test with: curl http://localhost:8080/

Features:
  - Declarative routing with get:do:, post:do:, etc.
  - URL parameters (:id, :name)
  - Query parameter access
  - JSON request/response handling
  - Middleware support
"

Smalltalk fileIn: 'lib/Http/Server.st'.
Smalltalk fileIn: 'lib/Http/Router.st'.

| app db nextId |

"Simple in-memory database"
db := Dict new.
db at: '1' put: #{ 'id' -> '1'. 'name' -> 'Alice'. 'email' -> 'alice@example.com' }.
db at: '2' put: #{ 'id' -> '2'. 'name' -> 'Bob'. 'email' -> 'bob@example.com' }.
nextId := 3.

app := WebApp new.

"--- Middleware: Request Logger ---"
app use: [:request :next |
    | start response |
    start := DateTime now.
    (request method, ' ', request path) printNl.
    response := next value: request.
    response
].

"--- Routes ---"

"GET / - Welcome page"
app get: '/' do: [:req :params |
    HttpResponse ok: 'Welcome to Lambda Smalltalk REST API!

Available endpoints:
  GET    /users          - List all users
  GET    /users/:id      - Get user by ID
  POST   /users          - Create new user (JSON body: {"name": "...", "email": "..."})
  PUT    /users/:id      - Update user
  DELETE /users/:id      - Delete user
  GET    /search?q=...   - Search users by name
'
].

"GET /users - List all users"
app get: '/users' do: [:req :params |
    | users |
    users := Array new: 0.
    db keysAndValuesDo: [:k :v | users := users copyWith: v].
    HttpResponse json: users
].

"GET /users/:id - Get single user"
app get: '/users/:id' do: [:req :params |
    | id user |
    id := params at: 'id'.
    user := db at: id ifAbsent: [ nil ].

    user ifNil: [
        HttpResponse status: 404 text: 'Not Found' body: ('User not found: ', id)
    ] ifNotNil: [
        HttpResponse json: user
    ]
].

"POST /users - Create new user"
app post: '/users' do: [:req :params |
    | body name email id user |
    body := req jsonBody.
    body ifNil: [
        ^ HttpResponse badRequest: 'Invalid JSON body'
    ].

    name := body at: 'name' ifAbsent: [ nil ].
    email := body at: 'email' ifAbsent: [ nil ].

    (name isNil or: [email isNil]) ifTrue: [
        ^ HttpResponse badRequest: 'Missing required fields: name, email'
    ].

    id := nextId asString.
    nextId := nextId + 1.
    user := Dict new.
    user at: 'id' put: id.
    user at: 'name' put: name.
    user at: 'email' put: email.
    db at: id put: user.

    HttpResponse status: 201 text: 'Created' body: (Json generate: user)
].

"PUT /users/:id - Update user"
app put: '/users/:id' do: [:req :params |
    | id body user |
    id := params at: 'id'.
    user := db at: id ifAbsent: [ nil ].

    user ifNil: [
        ^ HttpResponse status: 404 text: 'Not Found' body: ('User not found: ', id)
    ].

    body := req jsonBody.
    body ifNil: [
        ^ HttpResponse badRequest: 'Invalid JSON body'
    ].

    "Update fields"
    body keysAndValuesDo: [:k :v |
        (k = 'id') ifFalse: [ user at: k put: v ]
    ].

    HttpResponse json: user
].

"DELETE /users/:id - Delete user"
app delete: '/users/:id' do: [:req :params |
    | id user |
    id := params at: 'id'.
    user := db at: id ifAbsent: [ nil ].

    user ifNil: [
        ^ HttpResponse status: 404 text: 'Not Found' body: ('User not found: ', id)
    ].

    db removeKey: id.
    HttpResponse json: #{ 'status' -> 'deleted'. 'id' -> id }
].

"GET /search?q=... - Search users"
app get: '/search' do: [:req :params |
    | query results |
    query := (params at: 'q' ifAbsent: [ '' ]) asLowercase.

    results := Array new: 0.
    db keysAndValuesDo: [:k :v |
        | name |
        name := (v at: 'name') asLowercase.
        (name includes: query) ifTrue: [
            results := results copyWith: v
        ]
    ].

    HttpResponse json: results
].

"GET /echo/:message - Echo back the message"
app get: '/echo/:message' do: [:req :params |
    HttpResponse ok: (params at: 'message')
].

"--- Start Server ---"
('Routes registered: ', app routeCount asString) printNl.
'Starting server on http://localhost:8080' printNl.
'Press Ctrl+C to stop.' printNl.
'' printNl.

app listen: 8080.

29_compress.st

"=== Lambda Smalltalk: Compress Plugin ==="
"High-performance compression using Rust's flate2 (gzip) and zstd libraries"
"Features: gzip, zstd, parallel file compression with Rayon"

"=== Setup ==="
"First, build the plugin:"
"  cd examples/plugins/compress_plugin && cargo build --release"
"Then copy the DLL to plugins/:"
"  cp target/release/compress_plugin.dll ../../plugins/"

'Loading compress plugin...' printNl.
Plugin load: 'compress'.

"=== Create test file ==="
| testFile testContent compressed decompressed |
testFile := 'examples/temp_compress_test.txt'.
testContent := 'Hello, Lambda Smalltalk! This is a test file for compression. '.
testContent := testContent , testContent , testContent , testContent.
testContent := testContent , testContent , testContent , testContent.
(File write: testFile content: testContent).
('Created test file: ' , testFile , ' (' , testContent size asString , ' bytes)') printNl.

"=== In-memory compression ==="
'--- In-memory gzip compression ---' printNl.
compressed := Plugin call: 'compress' function: 'gzip_compress' args: { testContent }.
('Compressed to ' , compressed size asString , ' bytes') printNl.

decompressed := Plugin call: 'compress' function: 'gzip_decompress' args: { compressed }.
('Decompressed: ' , decompressed size asString , ' bytes') printNl.
('Match: ' , (decompressed = testContent) asString) printNl.

'--- In-memory zstd compression (level 3) ---' printNl.
compressed := Plugin call: 'compress' function: 'zstd_compress' args: { testContent. 3 }.
('Compressed to ' , compressed size asString , ' bytes') printNl.

decompressed := Plugin call: 'compress' function: 'zstd_decompress' args: { compressed }.
('Decompressed: ' , decompressed size asString , ' bytes') printNl.

"=== File compression ==="
| gzFile zstFile restoredFile |
gzFile := testFile , '.gz'.
zstFile := testFile , '.zst'.
restoredFile := testFile , '.restored'.

'--- File-based gzip compression ---' printNl.
(Plugin call: 'compress' function: 'gzip_file' args: { testFile. gzFile }).
('Created: ' , gzFile) printNl.

'--- File-based zstd compression ---' printNl.
(Plugin call: 'compress' function: 'zstd_file' args: { testFile. zstFile. 3 }).
('Created: ' , zstFile) printNl.

"=== Decompression ==="
'--- Decompress gzip file ---' printNl.
(Plugin call: 'compress' function: 'gunzip_file' args: { gzFile. restoredFile }).
('Restored file matches: ' , ((File read: restoredFile) = testContent) asString) printNl.

"=== Parallel compression (multiple files) ==="
'--- Creating multiple test files ---' printNl.
| files |
files := OrderedCollection new.
1 to: 4 do: [:i |
    | fname |
    fname := 'examples/temp_parallel_' , i asString , '.txt'.
    (File write: fname content: ('File ' , i asString , ': ' , testContent)).
    files add: fname.
].

'--- Parallel gzip compression (4 files using Rayon) ---' printNl.
(Plugin call: 'compress' function: 'gzip_parallel' args: { files asArray }) printNl.

'--- Parallel zstd compression (4 files using Rayon) ---' printNl.
(Plugin call: 'compress' function: 'zstd_parallel' args: { files asArray. 3 }) printNl.

"=== Cleanup ==="
'--- Cleaning up ---' printNl.
(File delete: testFile).
(File delete: gzFile).
(File delete: zstFile).
(File delete: restoredFile).
1 to: 4 do: [:i |
    | fname gzName zstName |
    fname := 'examples/temp_parallel_' , i asString , '.txt'.
    gzName := fname , '.gz'.
    zstName := fname , '.zst'.
    (File delete: fname).
    (File delete: gzName).
    (File delete: zstName).
].

'=== Done! ===' printNl.

30_uuid.st

"=== Lambda Smalltalk: UUID Plugin ==="
"UUID generation using the lambda-st-plugin toolkit"
"This plugin demonstrates how easy it is to create plugins with the toolkit"

"=== Setup ==="
"Build the plugin:"
"  cd examples/plugins/uuid_plugin && cargo build --release"
"Copy to plugins/:"
"  cp target/release/uuid_plugin.dll ../../plugins/"

'Loading uuid plugin...' printNl.
Plugin load: 'uuid'.

"=== Generate UUIDs ==="
'--- UUID v4 (random) ---' printNl.
| uuid1 uuid2 |
uuid1 := Plugin call: 'uuid' function: 'v4' args: #().
uuid2 := Plugin call: 'uuid' function: 'v4' args: #().
('UUID v4 #1: ' , uuid1) printNl.
('UUID v4 #2: ' , uuid2) printNl.
('Different: ' , (uuid1 ~= uuid2) asString) printNl.

'--- UUID v7 (time-ordered) ---' printNl.
| uuid3 uuid4 |
uuid3 := Plugin call: 'uuid' function: 'v7' args: #().
uuid4 := Plugin call: 'uuid' function: 'v7' args: #().
('UUID v7 #1: ' , uuid3) printNl.
('UUID v7 #2: ' , uuid4) printNl.
('v7 is time-ordered, so #1 < #2: ' , (uuid3 < uuid4) asString) printNl.

"=== Parse and validate ==="
'--- Parse UUID ---' printNl.
| parsed |
parsed := Plugin call: 'uuid' function: 'parse' args: #('550e8400-e29b-41d4-a716-446655440000').
('Parsed: ' , parsed) printNl.

'--- Validate UUIDs ---' printNl.
| valid1 valid2 |
valid1 := Plugin call: 'uuid' function: 'is_valid' args: #('550e8400-e29b-41d4-a716-446655440000').
valid2 := Plugin call: 'uuid' function: 'is_valid' args: #('not-a-uuid').
('Valid UUID: ' , valid1 asString) printNl.
('Invalid string: ' , valid2 asString) printNl.

'--- Get UUID version ---' printNl.
| ver4 ver7 |
ver4 := Plugin call: 'uuid' function: 'version' args: { uuid1 }.
ver7 := Plugin call: 'uuid' function: 'version' args: { uuid3 }.
('Version of uuid1 (v4): ' , ver4 asString) printNl.
('Version of uuid3 (v7): ' , ver7 asString) printNl.

'=== Done! ===' printNl.

31_crypto.st

"=== Lambda Smalltalk: Crypto Plugin ==="
"Password hashing with bcrypt and Argon2"
"Security-critical operations that require native implementation"

"=== Setup ==="
"Build the plugin:"
"  cd examples/plugins/crypto_plugin && cargo build --release"
"Copy to plugins/:"
"  cp target/release/crypto_plugin.dll ../../plugins/"

'Loading crypto plugin...' printNl.
Plugin load: 'crypto'.

"=== bcrypt ==="
'--- bcrypt hashing ---' printNl.
| password bcryptHash |
password := 'mySecretPassword123'.

bcryptHash := Plugin call: 'crypto' function: 'bcrypt_hash' args: { password }.
('bcrypt hash: ' , bcryptHash) printNl.

'--- bcrypt verification ---' printNl.
| validResult invalidResult |
validResult := Plugin call: 'crypto' function: 'bcrypt_verify' args: { password. bcryptHash }.
invalidResult := Plugin call: 'crypto' function: 'bcrypt_verify' args: { 'wrongPassword'. bcryptHash }.
('Correct password: ' , validResult asString) printNl.
('Wrong password: ' , invalidResult asString) printNl.

'--- bcrypt with custom cost ---' printNl.
| lowCostHash |
lowCostHash := Plugin call: 'crypto' function: 'bcrypt_hash_cost' args: { password. 4 }.
('Low cost (4) hash: ' , lowCostHash) printNl.

"=== Argon2 ==="
'--- Argon2id hashing (recommended) ---' printNl.
| argon2Hash |
argon2Hash := Plugin call: 'crypto' function: 'argon2_hash' args: { password }.
('Argon2 hash: ' , argon2Hash) printNl.

'--- Argon2 verification ---' printNl.
| argonValid argonInvalid |
argonValid := Plugin call: 'crypto' function: 'argon2_verify' args: { password. argon2Hash }.
argonInvalid := Plugin call: 'crypto' function: 'argon2_verify' args: { 'wrongPassword'. argon2Hash }.
('Correct password: ' , argonValid asString) printNl.
('Wrong password: ' , argonInvalid asString) printNl.

'=== Done! ===' printNl.

32_serverless.st

"
Lambda Smalltalk - Serverless Examples

This file demonstrates how to run Lambda Smalltalk in various serverless environments:
1. AWS Lambda (Custom Runtime)
2. Google Cloud Run / Knative (HTTP Server)
3. OpenFaaS (stdin/stdout - already works with CGI-style)

Note: These examples are meant to be run in their respective environments.
      Running them locally will show how the code looks, but won't actually work
      without the proper runtime environment.
"

'=== Serverless Examples ===' printNl.
'' printNl.

'--- 1a. AWS Lambda with WebApp (API Gateway) ---' printNl.
'
Use Lambda.runHttp: to connect WebApp router to API Gateway events.

handler.st:
  Module import: ''Http''.
  Module import: ''Lambda''.

  | app |
  app := WebApp new.

  app get: ''/'' do: [:req :params |
      HttpResponse ok: ''Hello from Lambda!''
  ].

  app get: ''/users/:id'' do: [:req :params |
      | id |
      id := params at: ''id''.
      HttpResponse json: #{ ''id'' -> id. ''name'' -> ''Alice'' }
  ].

  app post: ''/users'' do: [:req :params |
      | body |
      body := req jsonBody.
      HttpResponse json: #{ ''status'' -> ''created''. ''name'' -> (body at: ''name'') }
  ].

  Lambda runHttp: app.

Dockerfile:
  FROM amazonlinux:2
  COPY lambda-st /usr/bin/
  COPY handler.st lib/ /var/runtime/
  WORKDIR /var/runtime
  CMD ["lambda-st", "run", "handler.st"]
' printNl.

'' printNl.
'--- 1b. AWS Lambda Low-Level (Direct Event Handling) ---' printNl.
'
For full control over event processing, use Lambda.run: directly.

handler.st:
  Module import: ''Lambda''.

  Lambda run: [:event :context |
      | name |
      name := (event at: ''name'') ifNil: [''World''].
      ^ Dict new
          at: ''statusCode'' put: 200;
          at: ''body'' put: (''Hello, '', name, ''!'');
          yourself
  ].
' printNl.

'' printNl.
'--- 2. Google Cloud Run / Knative ---' printNl.
'
Cloud Run and Knative expect an HTTP server on the port specified
by the PORT environment variable (default 8080).

server.st:
  Smalltalk fileIn: ''lib/Http/Server.st''.

  | port |
  port := (Env at: ''PORT'') ifNil: [''8080''].
  HttpServer start: port asInteger handler: [:request |
      (request path = ''/'') ifTrue: [
          ^ HttpResponse ok: ''Hello from Lambda Smalltalk!''
      ].
      (request path = ''/health'') ifTrue: [
          ^ HttpResponse json: (Dict new at: ''status'' put: ''healthy''; yourself)
      ].
      ^ HttpResponse notFound
  ].

Dockerfile:
  FROM debian:slim
  COPY lambda-st /usr/bin/
  COPY server.st lib/ /app/
  WORKDIR /app
  CMD ["lambda-st", "run", "server.st"]
' printNl.

'' printNl.
'--- 3. OpenFaaS (stdin/stdout) ---' printNl.
'
OpenFaaS classic mode uses stdin/stdout, which already works with
Lambda Smalltalk without any special library.

handler.st:
  | input data result |
  input := Stdin readAll.
  data := Json parse: input.

  result := Dict new
      at: ''message'' put: (''Hello, '', ((data at: ''name'') ifNil: [''World'']), ''!'');
      yourself.

  (Json generate: result) printNl.
' printNl.

'' printNl.
'--- API Reference ---' printNl.

'Lambda (AWS):' printNl.
'  Lambda run: handlerBlock        - Main event loop (direct event handling)' printNl.
'  Lambda runSimple: handlerBlock  - Auto-wrap response as JSON' printNl.
'  Lambda runHttp: webApp          - Run WebApp with API Gateway events' printNl.
'' printNl.

'HttpServer (Cloud Run/Knative):' printNl.
'  HttpServer start: port handler: block  - Start HTTP server' printNl.
'' printNl.

'HttpRequest (inside handler):' printNl.
'  request method                  - GET, POST, etc.' printNl.
'  request path                    - /api/users' printNl.
'  request headers                 - Dict of headers' printNl.
'  request headerAt: name          - Get specific header' printNl.
'  request queryParams             - Dict of query params' printNl.
'  request body                    - Raw body string' printNl.
'  request jsonBody                - Parse body as JSON' printNl.
'' printNl.

'HttpResponse (return from handler):' printNl.
'  HttpResponse ok: body           - 200 OK' printNl.
'  HttpResponse json: object       - 200 with JSON body' printNl.
'  HttpResponse notFound           - 404 Not Found' printNl.
'  HttpResponse badRequest: msg    - 400 Bad Request' printNl.
'  HttpResponse internalError: msg - 500 Internal Server Error' printNl.
'  HttpResponse redirect: url      - 302 Redirect' printNl.
'' printNl.

'=== Example Complete ===' printNl.

33_callback_to_async.st

"
Callback to Async/Await Transformer

Demonstrates transforming callback-style JavaScript code
to modern async/await syntax using Lambda Smalltalk's Grammar.

This is NOT simple text replacement - it restructures the entire code:
- Removes callback parameters from function signatures
- Converts nested callback functions to sequential await statements
- Transforms error handling from callback(err) to throw/try-catch

Uses transformTree: for recursive tree-based transformation.
"

Module import: 'Transform'.

'=== Callback to Async/Await Transformer ===' printNl.
'' printNl.

| callbackCode |
callbackCode := 'function loadUser(id, callback) {
    db.query(sql, id, function(err, rows) {
        if (err) { callback(err, null); return; }
        callback(null, rows[0]);
    });
}

function saveUser(user, callback) {
    db.insert(table, user, function(err, result) {
        if (err) { callback(err, null); return; }
        callback(null, result.id);
    });
}
'.

'--- INPUT (callback style) ---' printNl.
callbackCode printNl.

"Grammar with nested block support"
| grammar transformer result |

grammar := Grammar from: '
    callback_func: "function" WS @name NAME "(" @params PARAMS "," WS "callback" ")" WS @body block
    block: "{" content* "}"
    content: block | NOTBRACE
    NAME: /[a-zA-Z_][a-zA-Z0-9_]*/
    PARAMS: /[a-zA-Z_][a-zA-Z0-9_]*/
    NOTBRACE: /[^{}]+/
    WS: /\s*/
    %ignore /./
'.

transformer := Transformer new grammar: grammar.

"Rule for callback_func - receives ParseTree node and transform helper"
transformer rule: 'callback_func' do: [:node :transformChild |
    | name params bodyNode bodyText |

    "Use ParseTree API to extract named captures directly"
    name := node captureNamed: 'name'.
    params := node captureNamed: 'params'.

    "Get the body capture node for recursive transformation"
    bodyNode := node captureNodeNamed: 'body'.

    "Transform body recursively"
    bodyText := bodyNode notNil
        ifTrue: [transformChild value: bodyNode]
        ifFalse: ['{}'].

    "Build async function"
    'async function ' , name , '(' , params , ') ' , bodyText
].

"Rule for transforming block content"
transformer rule: 'block' do: [:node :transformChild |
    | children stream |
    children := node children.
    stream := StringStream new.

    children do: [:child |
        stream nextPutAll: (transformChild value: child).
    ].

    stream contents
].

result := transformer transformTree: callbackCode.

"Post-process: convert callback patterns to async patterns"
"Step 1: callback(null, X) -> return X"
| g1 |
g1 := Grammar from: '
    ok: "callback(null, " @val VAL ")"
    VAL: /[a-zA-Z0-9_.\[\]]+/
    %ignore /./
'.
result := Grammar replace: g1 in: result with: [:m |
    'return ' , (m at: 'val')
].

"Step 2: callback(err, null); return; -> throw err;"
| g2 |
g2 := Grammar from: '
    err: "callback(err, null); return;"
    %ignore /./
'.
result := Grammar replace: g2 in: result with: [:m | 'throw err;' ].

"Step 3: if (err) { throw err; } -> (remove)"
| g3 |
g3 := Grammar from: '
    iferr: "if (err) { throw err; }"
    %ignore /./
'.
result := Grammar replace: g3 in: result with: [:m | '' ].

"Step 4: db.method(a, b, function(err, res) { BODY }); -> const res = await db.method(a, b); BODY"
| g4 |
g4 := Grammar from: '
    dbcall: @obj IDENT "." @method IDENT "(" @arg1 IDENT ", " @arg2 IDENT ", function(err, " @res IDENT ") {" WS @body BODY WS "});"
    IDENT: /[a-zA-Z_][a-zA-Z0-9_]*/
    BODY: /[^}]+/
    WS: /\s*/
    %ignore /./
'.
result := Grammar replace: g4 in: result with: [:m |
    'const ' , (m at: 'res') , ' = await ' , (m at: 'obj') , '.' , (m at: 'method') , '(' , (m at: 'arg1') , ', ' , (m at: 'arg2') , ');
        ' , (m at: 'body') trim
].

'--- OUTPUT (async/await style) ---' printNl.
result printNl.

'=== Transformation Complete ===' printNl.

34_expression_parser.st

"
Expression Parser with Left-Recursive Grammar

This example demonstrates parsing arithmetic expressions using
left-recursive grammar, which requires the Earley parser.
Left recursion is essential for handling operator precedence correctly.
"

'=== Expression Parser Example ===' printNl.
'' printNl.


"--- Example 1: Basic arithmetic expression parsing ---"
'Example 1: Parse arithmetic expressions' printNl.

| g source matches |

"Left-recursive grammar for arithmetic expressions"
g := Grammar from: '
    expr: expr "+" term | term
    term: NUMBER
    NUMBER: /[0-9]+/
'.

"Extract expressions from simple source"
source := '1+2+3'.
matches := Grammar findAll: g in: source.

matches size print. ' expressions found' printNl.
matches do: [:m |
    ('  ' , (m at: 'text')) printNl.
].
'' printNl.


"--- Example 2: Extract expressions with captures ---"
'Example 2: Extract with captures' printNl.

| g2 source2 matches2 |

"Grammar with captures for simple addition"
g2 := Grammar from: '
    expr: @left NUMBER "+" @right NUMBER
    NUMBER: /[0-9]+/
'.

source2 := '10+20'.
matches2 := Grammar findAll: g2 in: source2.

matches2 do: [:m |
    | left right |
    left := m at: 'left'.
    right := m at: 'right'.
    ('  Found: ' , (m at: 'text')) printNl.
    ('    left=' , left , ', right=' , right) printNl.
].
'' printNl.


"--- Example 3: Nested expression parsing ---"
'Example 3: Parse nested expressions' printNl.

| g3 source3 matches3 |

"Grammar for nested parenthesized expressions"
g3 := Grammar from: '
    expr: expr "+" term | term
    term: "(" expr ")" | NUMBER
    NUMBER: /[0-9]+/
'.

source3 := '(1+2)+(3+4)'.
matches3 := Grammar findAll: g3 in: source3.

matches3 size print. ' nested expressions found' printNl.
matches3 do: [:m |
    ('  ' , (m at: 'text')) printNl.
].
'' printNl.


"--- Example 4: Chain of operations (left-associative) ---"
'Example 4: Left-associative operations' printNl.

| g4 source4 matches4 |

"Left recursion ensures left-associative parsing"
g4 := Grammar from: '
    chain: chain "." method | IDENT
    method: IDENT "()"
    IDENT: /[a-z]+/
'.

source4 := 'obj.foo().bar().baz()'.
matches4 := Grammar findAll: g4 in: source4.

matches4 size print. ' method chains found' printNl.
matches4 do: [:m |
    ('  ' , (m at: 'text')) printNl.
].
'' printNl.


'=== Note ===' printNl.
'Left-recursive grammar requires Earley parser.' printNl.
'PEG parser would infinite-loop on left recursion.' printNl.
'The grammar system automatically selects Earley when it detects left recursion.' printNl.
'' printNl.

'=== Done ===' printNl.

35_format_each.st

"
formatEach: / formatEach:join: - High-performance template expansion

These primitives apply Mustache templates to arrays of Dicts entirely in Rust,
eliminating Smalltalk block overhead. Best for:
- Large datasets (1000+ items)
- Simple template expansion
- Output-heavy tasks (CSV→HTML, JSON→report, etc.)

Usage:
    arrayOfDicts formatEach: 'template {{key}}'           => Array of strings
    arrayOfDicts formatEach: 'template {{key}}' join: ',' => Single joined string

Note: Use triple braces {{{key}}} to disable HTML escaping.
"

'=== formatEach: Demo ===' printNl.
'' printNl.

"--- Basic usage ---"
'1. Basic template expansion:' printNl.
| users |
users := OrderedCollection new.
users add: #{'name' -> 'Alice'. 'age' -> '30'}.
users add: #{'name' -> 'Bob'. 'age' -> '25'}.
users add: #{'name' -> 'Carol'. 'age' -> '35'}.

(users asArray formatEach: '  - {{name}} (age {{age}})' join: '
') printNl.
'' printNl.

"--- HTML table generation ---"
'2. CSV to HTML table:' printNl.
| csvData lines rows |
csvData := 'Name,Department,Salary
Alice,Engineering,80000
Bob,Marketing,65000
Carol,Engineering,90000
David,Sales,70000
Eve,Engineering,85000'.

lines := csvData split: '
'.
rows := OrderedCollection new.
2 to: lines size do: [:i |
    | line cols |
    line := lines at: i.
    cols := line split: ','.
    cols size >= 3 ifTrue: [
        rows add: #{
            'name' -> (cols at: 1).
            'dept' -> (cols at: 2).
            'salary' -> (cols at: 3)
        }.
    ].
].

'<table>' printNl.
'  <tr><th>Name</th><th>Department</th><th>Salary</th></tr>' printNl.
(rows asArray formatEach: '  <tr><td>{{{name}}}</td><td>{{{dept}}}</td><td>${{{salary}}}</td></tr>' join: '
') printNl.
'</table>' printNl.
'' printNl.

"--- Performance comparison ---"
'3. Performance comparison (1000 items):' printNl.
| data start1 end1 start2 end2 result1 result2 |

"Generate test data"
data := OrderedCollection new.
1 to: 1000 do: [:i |
    data add: #{
        'id' -> i asString.
        'value' -> (i * 2) asString
    }.
].
data := data asArray.

"Method 1: do: loop with string concatenation"
start1 := Time millisecondClock.
result1 := ''.
data do: [:item |
    result1 := result1 , 'ID: ' , (item at: 'id') , ', Value: ' , (item at: 'value') , '
'.
].
end1 := Time millisecondClock.

"Method 2: formatEach:join:"
start2 := Time millisecondClock.
result2 := data formatEach: 'ID: {{id}}, Value: {{value}}' join: '
'.
end2 := Time millisecondClock.

('  do: loop:        ' , (end1 - start1) asString , 'ms') printNl.
('  formatEach:join: ' , (end2 - start2) asString , 'ms') printNl.
(end1 - start1) > 0 ifTrue: [
    ('  Speedup: ' , (((end1 - start1) asFloat / ((end2 - start2) max: 1) asFloat) roundTo: 0.1) asString , 'x') printNl.
].
'' printNl.

"--- JSON-like output ---"
'4. JSON array generation:' printNl.
| items jsonArray |
items := OrderedCollection new.
items add: #{'key' -> 'host'. 'value' -> 'localhost'}.
items add: #{'key' -> 'port'. 'value' -> '8080'}.
items add: #{'key' -> 'debug'. 'value' -> 'true'}.

jsonArray := '[
' , (items asArray formatEach: '  {"{{key}}": "{{value}}"}' join: ',
') , '
]'.
jsonArray printNl.
'' printNl.

"--- Markdown list ---"
'5. Markdown checklist:' printNl.
| tasks |
tasks := OrderedCollection new.
tasks add: #{'task' -> 'Implement formatEach:'. 'done' -> 'x'}.
tasks add: #{'task' -> 'Write tests'. 'done' -> 'x'}.
tasks add: #{'task' -> 'Update docs'. 'done' -> ' '}.
tasks add: #{'task' -> 'Release'. 'done' -> ' '}.

(tasks asArray formatEach: '- [{{done}}] {{task}}' join: '
') printNl.
'' printNl.

'=== Done ===' printNl.

36_named_pipe.st

"=== Lambda Smalltalk: Named Pipe Client Plugin ==="
"Connect to Windows named pipes for inter-process communication"

"=== Setup ==="
"Build the plugin:"
"  cargo build -p named_pipe_plugin"
"Copy to plugins/:"
"  cp target/debug/named_pipe_plugin.dll plugins/"

"=== Test with a PowerShell echo pipe server ===
 Run this in a separate PowerShell terminal first:
   $pipe = [System.IO.Pipes.NamedPipeServerStream]::new('LambdaStTest', 'InOut')
   $pipe.WaitForConnection()
   $reader = [System.IO.StreamReader]::new($pipe)
   $writer = [System.IO.StreamWriter]::new($pipe)
   $writer.AutoFlush = $true
   $line = $reader.ReadLine()
   Write-Host ('Received: ' + $line)
   $writer.WriteLine('Echo: ' + $line)
   $pipe.Dispose()
 Note: Smalltalk comments cannot contain double-quote characters (no escape).
 The original PowerShell snippet used double-quoted strings; we use
 single-quoted concatenation here so the comment parses cleanly."

'Loading named_pipe plugin...' printNl.
Plugin load: 'named_pipe'.

"Connect to the test pipe"
| pipe |
pipe := Plugin call: 'named_pipe' function: 'connect'
    args: #('\\.\pipe\LambdaStTest').
('Connected, handle: ' , pipe asString) printNl.

"Check connection status"
| connected |
connected := Plugin call: 'named_pipe' function: 'is_connected'
    args: { pipe }.
('Is connected: ' , connected asString) printNl.

"Write a message (with newline so the server's ReadLine completes)"
| written |
written := Plugin call: 'named_pipe' function: 'write'
    args: { pipe. 'Hello from Lambda Smalltalk!
' }.
('Wrote ' , written asString , ' bytes') printNl.

"Read the echo response"
| response |
response := Plugin call: 'named_pipe' function: 'read_line'
    args: { pipe }.
('Response: ' , response) printNl.

"Close connection"
Plugin call: 'named_pipe' function: 'close' args: { pipe }.
'Connection closed.' printNl.

'=== Done! ===' printNl.

37_sqlserver.st

"
Lambda Smalltalk - SQL Server Plugin Example
Requires: sqlserver_plugin.dll and a running SQL Server instance

NOTE: This example requires a SQL Server database.
If you don't have one, install SQL Server Express or use Docker:
  docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=YourStrong!Pass' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2022-latest

Connection string format (ADO.NET):
  'Server=tcp:localhost,1433;Database=master;User ID=sa;Password=YourStrong!Pass;TrustServerCertificate=true'
"

| db rows |

'--- Loading SQL Server Library ---' printNl.
Smalltalk fileIn: 'examples/lib/sqlserver.st'.

'--- Connecting to SQL Server ---' printNl.
"Change this connection string to match your SQL Server setup"
db := SqlServer connect: 'Server=tcp:localhost,1433;Database=master;User ID=sa;Password=YourStrong!Pass;TrustServerCertificate=true'.
db = nil ifTrue: [
    'ERROR: Could not connect to SQL Server' printNl.
    'Make sure SQL Server is running and connection string is correct' printNl.
    ^ self
].
'Connected successfully' printNl.

'--- Creating Test Table ---' printNl.
db execute: 'IF OBJECT_ID(''test_users'', ''U'') IS NOT NULL DROP TABLE test_users'.
db execute: 'CREATE TABLE test_users (id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(100), age INT)'.
'Table created' printNl.

'--- Inserting Data ---' printNl.
db execute: 'INSERT INTO test_users (name, age) VALUES (''Alice'', 30)'.
db execute: 'INSERT INTO test_users (name, age) VALUES (''Bob'', 25)'.
'Data inserted' printNl.

'--- Inserting with Parameters ---' printNl.
db execute: 'INSERT INTO test_users (name, age) VALUES (@P1, @P2)' params: #('Charlie' 35).
'Parameterized insert successful' printNl.

'--- Querying All Rows ---' printNl.
rows := db query: 'SELECT id, name, age FROM test_users ORDER BY id'.
rows = nil ifTrue: [
    'Query failed' printNl. ^ self
].

'Query results:' printNl.
rows do: [:row |
    'ID: ' print. (row at: 'id') print.
    ', Name: ' print. (row at: 'name') print.
    ', Age: ' print. (row at: 'age') printNl.
].

'--- Querying with Parameters ---' printNl.
rows := db queryWith: 'SELECT name, age FROM test_users WHERE age > @P1' params: #(28).
rows = nil ifTrue: [
    'Parameterized query failed' printNl. ^ self
].

'Users older than 28:' printNl.
rows do: [:row |
    (row at: 'name') print. ' (age: ' print. (row at: 'age') print. ')' printNl.
].

'--- Cleanup ---' printNl.
db execute: 'DROP TABLE test_users'.
'Table dropped' printNl.

db close.
'--- SQL Server Plugin Test Complete ---' printNl.

38_oracle.st

"=== Lambda Smalltalk: Oracle Plugin Example ==="
"Demonstrates Oracle database connection and queries"

"NOTE: This example requires a running Oracle database."
"Requires Oracle Client (full or Instant Client) installed."

"=== Load the Oracle plugin ==="
'Loading Oracle plugin...' printNl.
| pluginName |
pluginName := Plugin load: 'plugins/oracle_plugin.dll'.
'Loaded: ' print. pluginName printNl.

'Available functions: ' print. (Plugin functions: 'oracle') printNl.

"=== Connect to database ==="
"connect takes 3 args: username, password, connect_string"
"connect_string examples: '//host:port/service_name' or TNS name"
| db |
db := Plugin call: 'oracle' function: 'connect' args: #('scott' 'tiger' '//localhost:1521/XEPDB1').

db ifNil: [
    'Failed to connect to Oracle. Make sure the server is running.' printNl.
] ifNotNil: [
    'Connected! Handle: ' print. db printNl.

    "=== Create a test table ==="
    | result |
    result := Plugin call: 'oracle' function: 'execute' args: { db.
        'CREATE TABLE test_users (
            id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
            name VARCHAR2(100) NOT NULL,
            age NUMBER,
            email VARCHAR2(100)
        )' }.
    'Create table: ' print. result printNl.

    "=== Insert data with parameters (Oracle uses :1, :2, ... for bind variables) ==="
    result := Plugin call: 'oracle' function: 'execute_params'
        args: { db. 'INSERT INTO test_users (name, age, email) VALUES (:1, :2, :3)'. #('Alice' 30 'alice@example.com') }.
    'Insert Alice: ' print. result printNl.

    result := Plugin call: 'oracle' function: 'execute_params'
        args: { db. 'INSERT INTO test_users (name, age, email) VALUES (:1, :2, :3)'. #('Bob' 25 'bob@example.com') }.
    'Insert Bob: ' print. result printNl.

    result := Plugin call: 'oracle' function: 'execute_params'
        args: { db. 'INSERT INTO test_users (name, age, email) VALUES (:1, :2, :3)'. #('Charlie' 35 'charlie@example.com') }.
    'Insert Charlie: ' print. result printNl.

    "=== Commit ==="
    result := Plugin call: 'oracle' function: 'commit' args: { db }.
    'Commit: ' print. result printNl.

    "=== Query all users ==="
    'All users:' printNl.
    | rows |
    rows := Plugin call: 'oracle' function: 'query' args: { db. 'SELECT * FROM test_users ORDER BY id' }.
    rows do: [:row |
        '  ' print. row printNl.
    ].

    "=== Query with parameters ==="
    'Users older than 28:' printNl.
    rows := Plugin call: 'oracle' function: 'query_params'
        args: { db. 'SELECT name, age FROM test_users WHERE age > :1'. #(28) }.
    rows do: [:row |
        '  ' print. (row at: 'NAME') print. ' is ' print. (row at: 'AGE') print. ' years old' printNl.
    ].

    "=== Update data ==="
    result := Plugin call: 'oracle' function: 'execute_params'
        args: { db. 'UPDATE test_users SET age = :1 WHERE name = :2'. #(31 'Alice') }.
    'Update Alice age: ' print. result printNl.

    "=== Delete data ==="
    result := Plugin call: 'oracle' function: 'execute_params'
        args: { db. 'DELETE FROM test_users WHERE name = :1'. #('Charlie') }.
    'Delete Charlie: ' print. result printNl.

    "=== Commit changes ==="
    Plugin call: 'oracle' function: 'commit' args: { db }.

    "=== Final state ==="
    'Final users:' printNl.
    rows := Plugin call: 'oracle' function: 'query' args: { db. 'SELECT * FROM test_users ORDER BY id' }.
    rows do: [:row |
        '  ' print. row printNl.
    ].

    "=== Cleanup ==="
    result := Plugin call: 'oracle' function: 'execute' args: { db. 'DROP TABLE test_users PURGE' }.
    'Drop table: ' print. result printNl.

    "=== Close connection ==="
    result := Plugin call: 'oracle' function: 'close' args: { db }.
    'Connection closed: ' print. result printNl.
].

'=== Done! ===' printNl.

39_zip.st

"=== Lambda Smalltalk: ZIP Archive Plugin ==="
"Full ZIP support: create, extract, directory archiving, and inspection"

Plugin load: 'compress'.

"=== Setup: create test files ==="
| workDir zipFile |
workDir := 'examples/temp_zip_test'.
zipFile  := 'examples/temp_zip_test/archive.zip'.
(File mkdir: workDir).

(File write: workDir , '/hello.txt'  content: 'Hello, Lambda Smalltalk!').
(File write: workDir , '/data.csv'   content: 'id,name,value\n1,foo,100\n2,bar,200').
(File write: workDir , '/readme.md'  content: '# Test\nThis is a test archive.').
'Created test files.' printNl.

"=== zip_create: create ZIP from file list (entry name = basename) ==="
| files |
files := { workDir , '/hello.txt'. workDir , '/data.csv'. workDir , '/readme.md' }.
(Plugin call: 'compress' function: 'zip_create' args: { zipFile. files }).
('Created: ' , zipFile) printNl.

"=== zip_list: list entries ==="
'--- zip_list ---' printNl.
| entries |
entries := Plugin call: 'compress' function: 'zip_list' args: { zipFile }.
entries do: [:name | ('  ' , name) printNl].

"=== zip_info: list entries with size info (tab-separated: name, size, compressed_size) ==="
'--- zip_info ---' printNl.
| info |
info := Plugin call: 'compress' function: 'zip_info' args: { zipFile }.
info do: [:line | ('  ' , line) printNl].

"=== zip_extract: extract all to a directory ==="
| extractDir |
extractDir := workDir , '/extracted'.
(File mkdir: extractDir).
(Plugin call: 'compress' function: 'zip_extract' args: { zipFile. extractDir }).
'--- zip_extract: extracted files ---' printNl.
| restored |
restored := Plugin call: 'compress' function: 'zip_list' args: { zipFile }.
restored do: [:name |
    | content |
    content := File read: extractDir , '/' , name.
    ('  ' , name , ': ' , content size asString , ' bytes') printNl.
].

"=== zip_extract_file: extract single file ==="
| singleDest |
singleDest := workDir , '/only_hello.txt'.
(Plugin call: 'compress' function: 'zip_extract_file'
    args: { zipFile. 'hello.txt'. singleDest }).
'--- zip_extract_file ---' printNl.
('  Extracted: ' , (File read: singleDest)) printNl.

"=== zip_create_mapped: custom entry names ==="
| mappedZip |
mappedZip := workDir , '/mapped.zip'.
(Plugin call: 'compress' function: 'zip_create_mapped'
    args: {
        mappedZip.
        { workDir , '/hello.txt'. workDir , '/data.csv' }.
        { 'docs/greeting.txt'. 'data/records.csv' }
    }).
'--- zip_create_mapped: entries with paths ---' printNl.
(Plugin call: 'compress' function: 'zip_list' args: { mappedZip })
    do: [:name | ('  ' , name) printNl].

"=== zip_add_dir: zip entire directory ==="
| srcDir dirZip |
srcDir := workDir , '/subdir'.
dirZip  := workDir , '/subdir.zip'.
(File mkdir: srcDir).
(File mkdir: srcDir , '/docs').
(File write: srcDir , '/main.txt'       content: 'Main file').
(File write: srcDir , '/docs/notes.txt' content: 'Notes here').
(Plugin call: 'compress' function: 'zip_add_dir' args: { dirZip. srcDir }).
'--- zip_add_dir: directory tree entries ---' printNl.
(Plugin call: 'compress' function: 'zip_list' args: { dirZip })
    do: [:name | ('  ' , name) printNl].

"=== Cleanup ==="
(File rmdir: workDir recursive: true).
'Cleanup done.' printNl.

'=== Done! ===' printNl.