Skip to content

Commit 7f467df

Browse files
author
Leo Louvar
committed
Fix version refs, immutable patterns, REPL engine ops, try/catch parsing
Made-with: Cursor
1 parent 9e980db commit 7f467df

5 files changed

Lines changed: 100 additions & 73 deletions

File tree

docs/Zixir Language complete guide.md

Lines changed: 53 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**A Comprehensive Guide for Beginners and Developers**
44

5-
*Version 1.0 - Production Ready*
5+
*Version 7.1.0 - Production Ready*
66

77
---
88

@@ -1723,15 +1723,16 @@ python "random" "randint" (1, 100) # Random int 1-100
17231723
let arr = [5, 2, 8, 1, 9]
17241724
let sorted = python "sorted" (arr) # [1, 2, 5, 8, 9]
17251725
1726-
# String manipulation (limited in v1.0)
1727-
# Note: Python string methods are not directly accessible
1728-
# Use Zixir string concatenation (++) instead
1726+
# For advanced string ops, use built-in functions:
1727+
# upper(), lower(), trim(), split(), join(), contains(), reverse()
1728+
# Or use Python for operations not covered by built-ins
17291729
```
17301730

17311731
**Date and Time:**
17321732
```zixir
1733-
# Note: datetime classes cannot be instantiated directly in Zixir v1.0
1734-
# Use Python via external scripts for datetime operations
1733+
# Datetime operations are handled via Python FFI:
1734+
# python "time" "time" () — current timestamp
1735+
# python "datetime" "now" () — requires external Python script
17351736
```
17361737

17371738
### When to Use Python vs Engine
@@ -1807,8 +1808,8 @@ let raw_temperatures = [
18071808
]
18081809
18091810
# Step 2: Clean the data (remove extreme outliers using engine operations)
1810-
# Note: In Zixir v1.0, complex filtering is best done with Python
1811-
# Here we'll demonstrate using engine.filter_gt for simple filtering
1811+
# Complex filtering is best done with engine ops or Python FFI
1812+
# Here we demonstrate using engine.filter_gt for simple filtering
18121813
fn remove_extreme_values(data: [Float], max_val: Float) -> [Float]: {
18131814
# Remove values above max using engine.filter_gt and subtraction
18141815
# This is a workaround - production code should use Python
@@ -1827,7 +1828,7 @@ fn remove_extreme_values(data: [Float], max_val: Float) -> [Float]: {
18271828
We define an array of temperature readings. For this example, assume data is pre-cleaned or use Python filtering.
18281829
18291830
**Step 2 - Data Cleaning (Approach):**
1830-
In Zixir v1.0, you have several options:
1831+
You have several options:
18311832
1. **Pre-clean data** before loading into Zixir
18321833
2. **Use Python** for complex filtering: `python "list" "filter" (lambda, data)`
18331834
3. **Use engine.filter_gt** for simple > filters
@@ -2036,7 +2037,7 @@ const MODEL = "gpt-3.5-turbo"
20362037
20372038
# Note: This project demonstrates the CONCEPT of LLM integration
20382039
# Actual implementation requires Python requests library and proper error handling
2039-
# In Zixir v1.0, complex nested Python calls may need to be done in Python scripts
2040+
# Complex nested Python calls may need to be done in Python scripts
20402041
20412042
# Step 1: Simple function to demonstrate the pattern
20422043
fn create_prompt(task: String, text: String) -> String: {
@@ -2121,7 +2122,7 @@ fn calculate_discount(order: Map) -> Map: {
21212122
}
21222123
21232124
# Note: This function demonstrates the concept
2124-
# In Zixir v1.0, membership testing requires Python or manual checks
2125+
# For membership testing, use contains() or engine operations
21252126
fn check_inventory(items: [String]) -> String: {
21262127
# Simplified: assume all items available for demo
21272128
"Items available: " ++ "laptop, mouse"
@@ -2150,7 +2151,7 @@ fn send_notification(customer: String, message: String) -> String: {
21502151
}
21512152
21522153
# Note: This project demonstrates workflow concepts
2153-
# In Zixir v1.0, use parallel arrays or Python for complex data structures
2154+
# For complex data structures, use maps or Python FFI
21542155
21552156
# Step 2: Main workflow orchestrator (simplified)
21562157
fn process_order_simplified(amount: Float, customer: String, method: String) -> String: {
@@ -2180,7 +2181,7 @@ process_order_simplified(1200.0, "Alice", "credit_card")
21802181
21812182
### Workflow Pattern Explanation
21822183
2183-
This demonstrates a **simplified Pipeline Pattern** suitable for Zixir v1.0:
2184+
This demonstrates a **Pipeline Pattern** using Zixir's functional approach:
21842185
21852186
**Step 1 - Input Validation:**
21862187
```zixir
@@ -2248,7 +2249,7 @@ let b = "test"
22482249
# Process data in batches using engine operations
22492250
let results = engine.map_mul(data, 2.0)
22502251
2251-
# Bad - individual calls (not supported in v1.0)
2252+
# Bad - individual calls in loops (prefer batch operations)
22522253
# Loops with Python calls are inefficient
22532254
```
22542255

@@ -2632,20 +2633,20 @@ python "random" "random" ()
26322633

26332634
**Filter array (using engine):**
26342635
```zixir
2635-
let filtered = []
2636-
for item in arr: {
2637-
if condition: {
2638-
filtered = filtered ++ [item]
2639-
}
2640-
}
2636+
let filtered = engine.filter_gt(arr, 0.0)
26412637
```
26422638

2643-
**Map operation:**
2639+
**Transform array (using pipe + engine):**
26442640
```zixir
2645-
let result = []
2646-
for item in arr: {
2647-
result = result ++ [transform(item)]
2648-
}
2641+
let result = engine.map_mul(arr, 2.0)
2642+
let stats = result |> engine.list_mean()
2643+
```
2644+
2645+
**Accumulate with recursion (functional style):**
2646+
```zixir
2647+
fn sum_list(arr):
2648+
if length(arr) == 0: 0
2649+
else: head(arr) + sum_list(tail(arr))
26492650
```
26502651

26512652
---
@@ -2664,49 +2665,37 @@ fn validate_age(age: Int) -> Bool: {
26642665
}
26652666
26662667
fn validate_user(user: Map) -> Map: {
2667-
let errors = []
2668-
2669-
if !validate_email(user["email"]): {
2670-
errors = errors ++ ["Invalid email"]
2671-
}
2672-
2673-
if !validate_age(user["age"]): {
2674-
errors = errors ++ ["Invalid age"]
2675-
}
2676-
2677-
if errors == []: {
2668+
let email_ok = validate_email(user["email"])
2669+
let age_ok = validate_age(user["age"])
2670+
2671+
if email_ok && age_ok: {
26782672
{"valid": true}
26792673
} else: {
2680-
{"valid": false, "errors": errors}
2674+
{"valid": false, "email_ok": email_ok, "age_ok": age_ok}
26812675
}
26822676
}
26832677
```
26842678

26852679
### Pattern 2: Retry Logic
26862680

26872681
```zixir
2688-
fn retry_operation(operation: Function, max_attempts: Int) -> Result: {
2689-
let attempts = 0
2690-
let result = null
2691-
2692-
while attempts < max_attempts && result == null: {
2693-
try {
2694-
result = operation()
2695-
} catch Error => {
2696-
attempts = attempts + 1
2697-
if attempts < max_attempts: {
2698-
# Wait before retry (using Python)
2699-
python "time.sleep" (1.0)
2700-
}
2701-
}
2702-
}
2703-
2704-
if result == null: {
2682+
# Recursive retry — no mutable state needed
2683+
fn retry_operation(operation: Function, attempts_left: Int) -> Map: {
2684+
if attempts_left <= 0: {
27052685
{"success": false, "error": "Max retries exceeded"}
27062686
} else: {
2707-
{"success": true, "result": result}
2687+
let result = operation()
2688+
if result["ok"]: {
2689+
{"success": true, "result": result["value"]}
2690+
} else: {
2691+
# Retry with one fewer attempt
2692+
retry_operation(operation, attempts_left - 1)
2693+
}
27082694
}
27092695
}
2696+
2697+
# Usage
2698+
let outcome = retry_operation(my_operation, 3)
27102699
```
27112700

27122701
### Pattern 3: Data Processing Pipeline
@@ -2738,14 +2727,16 @@ const DEFAULT_CONFIG = {
27382727
"debug": false
27392728
}
27402729
2741-
fn load_config(overrides: Map) -> Map: {
2742-
# Merge overrides with defaults
2743-
let config = DEFAULT_CONFIG
2744-
for key in python "overrides.keys" (): {
2745-
config[key] = overrides[key]
2746-
}
2747-
config
2730+
fn get_config(key: String, overrides: Map) -> Map: {
2731+
# Check overrides first, fall back to defaults
2732+
let override_val = overrides[key]
2733+
if override_val != nil: override_val
2734+
else: DEFAULT_CONFIG[key]
27482735
}
2736+
2737+
# Usage
2738+
let retries = get_config("max_retries", user_overrides)
2739+
let timeout = get_config("timeout", user_overrides)
27492740
```
27502741

27512742
### Pattern 5: Result Type
2.69 KB
Binary file not shown.

lib/zixir/interpreter.ex

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,30 @@ defmodule Zixir.Interpreter do
289289
end
290290
end
291291

292+
defp eval_expr({:try, body, catches, _line, _col}, env) do
293+
case eval_expr(body, env) do
294+
{:ok, value} -> {:ok, value}
295+
{:error, reason} ->
296+
case find_matching_catch(catches, reason, env) do
297+
{:ok, value} -> {:ok, value}
298+
:no_match -> {:error, reason}
299+
end
300+
end
301+
end
302+
292303
defp eval_expr(expr, _env) do
293304
{:error, "Unsupported expression: #{inspect(expr)}"}
294305
end
295306

307+
defp find_matching_catch([], _reason, _env), do: :no_match
308+
defp find_matching_catch([{error_var, _error_type, catch_body} | rest], reason, env) do
309+
catch_env = Map.put(env, error_var, reason)
310+
case eval_expr(catch_body, catch_env) do
311+
{:ok, value} -> {:ok, value}
312+
{:error, _} -> find_matching_catch(rest, reason, env)
313+
end
314+
end
315+
296316
# ============================================================================
297317
# Helper Functions
298318
# ============================================================================

lib/zixir/parser/unified.ex

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -479,16 +479,15 @@ defmodule Zixir.Parser.Unified do
479479

480480
defp parse_catches_impl(tokens, acc) do
481481
case tokens do
482-
[{:ident, error_var, _, _} | [{:op, :=, _, _}, {:op, :>, _, _} | rest_after_arrow]] ->
482+
[{:ident, error_var, _, _}, {:op, :fat_arrow, _, _} | rest_after_arrow] ->
483483
{error_type, rest} = parse_type(rest_after_arrow)
484484
{catch_body, rest2} = parse_block(rest)
485-
485+
486486
case rest2 do
487487
[{:op, :",", _, _} | after_comma] -> parse_catches_impl(after_comma, [{error_var, error_type, catch_body} | acc])
488-
[{:op, :"}", _, _} | _] -> {Enum.reverse([{error_var, error_type, catch_body} | acc]), rest2}
489488
_ -> {Enum.reverse([{error_var, error_type, catch_body} | acc]), rest2}
490489
end
491-
490+
492491
_ -> {Enum.reverse(acc), tokens}
493492
end
494493
end

lib/zixir/repl.ex

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,17 +255,34 @@ defmodule Zixir.REPL do
255255
end
256256
end
257257

258+
@engine_categories [
259+
{"Aggregations", ~w(list_sum list_product list_mean list_min list_max list_variance list_std)},
260+
{"Vector", ~w(dot_product vec_add vec_sub vec_mul vec_div vec_scale)},
261+
{"Transform", ~w(map_add map_mul filter_gt sort_asc)},
262+
{"Search", ~w(find_index count_value)},
263+
{"Matrix", ~w(mat_mul mat_transpose)},
264+
{"String", ~w(string_count string_find string_starts_with string_ends_with)}
265+
]
266+
258267
defp show_engine_ops() do
259268
ops = Zixir.Engine.operations()
260-
261269
IO.puts("Available engine operations (#{length(ops)} total):")
262270
IO.puts("")
263-
IO.puts("Aggregations: list_sum, list_product, list_mean, list_min, list_max, list_variance, list_std")
264-
IO.puts("Vector: dot_product, vec_add, vec_sub, vec_mul, vec_div, vec_scale")
265-
IO.puts("Transform: map_add, map_mul, filter_gt, sort_asc")
266-
IO.puts("Search: find_index, count_value")
267-
IO.puts("Matrix: mat_mul, mat_transpose")
268-
IO.puts("String: string_count, string_find, string_starts_with, string_ends_with")
271+
272+
Enum.each(@engine_categories, fn {category, known_ops} ->
273+
matching = Enum.filter(ops, &(Atom.to_string(&1) in known_ops))
274+
unless matching == [] do
275+
IO.puts("#{category}: #{Enum.map_join(matching, ", ", &Atom.to_string/1)}")
276+
end
277+
end)
278+
279+
uncategorized = Enum.reject(ops, fn op ->
280+
Enum.any?(@engine_categories, fn {_, known} -> Atom.to_string(op) in known end)
281+
end)
282+
unless uncategorized == [] do
283+
IO.puts("Other: #{Enum.map_join(uncategorized, ", ", &Atom.to_string/1)}")
284+
end
285+
269286
IO.puts("")
270287
IO.puts("Example: engine.list_sum([1.0, 2.0, 3.0])")
271288
end

0 commit comments

Comments
 (0)