Lambda Smalltalk examples/ ディレクトリの実動サンプル。各ファイルはそのまま実行可能。
実行方法: 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.