# Baba Yaga Syntax Gotchas This document catalogs the strict syntax requirements and common pitfalls discovered during development and testing of the `with` and `with rec` functionality. ## Table of Contents 1. [When Expression Syntax](#when-expression-syntax) 2. [With Block Syntax](#with-block-syntax) 3. [Operator Precedence and Parenthesization Rules](#operator-precedence-and-parenthesization-rules) 4. [Type System Requirements](#type-system-requirements) 5. [Function Definitions](#function-definitions) 6. [Data Structure Syntax](#data-structure-syntax) 7. [Common Error Patterns](#common-error-patterns) 8. [JavaScript Interop Gotchas](#javascript-interop-gotchas) ## When Expression Syntax ### **Incorrect: Using `else` keyword** ```baba // WRONG - Baba Yaga doesn't use 'else' status : when x is 0 then "zero" _ else "other"; ``` ### **Correct: Using `_ then` pattern** ```baba // CORRECT - Use '_ then' for fallback status : when x is 0 then "zero" _ then "other"; ``` ### **Incorrect: Nested when without proper structure** ```baba // WRONG - Missing 'then when' introduction status : when x is 0 then "zero" _ when x < 10 then "small" // Missing 'then when' _ then "large"; ``` ### **Correct: Proper nested when structure** ```baba // CORRECT - Use 'then when' for nested conditions status : when x is 0 then "zero" _ then when (x < 10) is true then "small" _ then "large"; ``` ### **Incorrect: Complex when without parentheses** ```baba // WRONG - Missing parentheses around complex conditions status : when x > 0 and x < 10 is true then "small" _ then "large"; ``` ### **Correct: Parentheses around complex conditions** ```baba // CORRECT - Wrap complex conditions in parentheses status : when (x > 0 and x < 10) is true then "small" _ then "large"; ``` ## With Block Syntax ### **Incorrect: Missing semicolons** ```baba // WRONG - Missing semicolon after when expression with ( status : when x > 0 is true then "positive" _ then "negative" // Missing ; ) -> status; ``` ### **Correct: Proper semicolon usage** ```baba // CORRECT - Semicolon after each entry with ( status : when x > 0 is true then "positive" _ then "negative"; ) -> status; ``` ### **Incorrect: with rec with non-function bindings** ```baba // WRONG - with rec only allows function bindings with rec ( x : 5; // Not a function! f : y -> y + 1; ) -> f x; ``` ### **Correct: with rec with function bindings only** ```baba // CORRECT - All bindings must be functions with rec ( f : x -> x + 1; g : y -> y * 2; ) -> f (g 5); ``` **Important**: `with rec` is **strictly function-only**. It does not allow any non-function bindings, even when functions are also present. This is by design to ensure mutual recursion works correctly. For non-function bindings, use regular `with` blocks instead. ### **Incorrect: Complex list literals with when expressions** ```baba // WRONG - Complex when expressions in list literals tags : [name, when age >= 18 is true then "adult" _ then "minor"]; ``` ### **Correct: Pre-compute values before list creation** ```baba // CORRECT - Compute values first, then create list with ( ageGroup : when age >= 18 is true then "adult" _ then "minor"; tags : [name, ageGroup]; ) -> tags; ``` ## Type System Requirements ### **Incorrect: Function type annotations** ```baba // WRONG - Function type annotations not supported factorial : (x: Int) -> Int -> x + 1; ``` ### **Correct: Simple type declarations** ```baba // CORRECT - Use simple type declarations factorial : x -> x + 1; ``` **Note**: While Baba Yaga supports function type annotations in function signatures (e.g., `add : (x: Int, y: Int) -> Int -> x + y;`), it does NOT support function type annotations in `with` blocks. See [Types Documentation](./04_types.md) for details on supported type annotations. ### **Incorrect: Non-existent functions** ```baba // WRONG - These functions don't exist in Baba Yaga str.toString x; // No str.toString function math.floor x; // No math.floor function type x; // No type function null; // No null value ``` ### **Correct: Use existing functions** ```baba // CORRECT - Use documented functions str.upper x; // str.upper exists math.sqrt x; // math.sqrt exists math.abs x; // math.abs exists ``` **Note**: For a complete list of available functions, see the [Types Documentation](./04_types.md) and [Crash Course](./00_crash-course.md). Baba Yaga has a focused set of built-in functions rather than trying to replicate all JavaScript functionality. ## Function Definitions ### **Incorrect: Missing arrow syntax** ```baba // WRONG - Missing -> arrow f x { x + 1 } ``` ### **Correct: Proper arrow syntax** ```baba // CORRECT - Use -> arrow f : x -> x + 1; ``` ### **Incorrect: Missing semicolon after function body** ```baba // WRONG - Missing semicolon f : x -> x + 1 // Missing ; ``` ### **Correct: Proper semicolon usage** ```baba // CORRECT - Semicolon after function definition f : x -> x + 1; ``` ## Operator Precedence and Parenthesization Rules Baba Yaga has strict requirements for operator precedence that differ from many other languages. Understanding these rules is crucial for writing correct code. ### **Operator Precedence Hierarchy (Highest to Lowest)** 1. **Function Calls and Member Access** - `f x`, `obj.property` 2. **Unary Operators** - `-x`, `!x` 3. **Arithmetic** - `*`, `/`, `%` (left-associative) 4. **Arithmetic** - `+`, `-` (left-associative) 5. **Comparison** - `=`, `!=`, `<`, `<=`, `>`, `>=` (non-associative) 6. **Logical** - `and`, `or` (left-associative) ### **Critical Rule: Function Calls in Comparisons** **Incorrect: Function calls without parentheses in comparisons** ```baba // WRONG - Function calls need parentheses in comparisons length list > 5; // Error: Unexpected token: OPERATOR (>) math.abs x < 0.001; // Error: Unexpected token: OPERATOR (<) str.length input >= 10; // Error: Unexpected token: OPERATOR (>=) ``` **Correct: Function calls wrapped in parentheses** ```baba // CORRECT - Wrap function calls in parentheses (length list) > 5; // ✓ Works correctly (math.abs x) < 0.001; // ✓ Works correctly (str.length input) >= 10; // ✓ Works correctly ``` ### **Critical Rule: Logical Operator Precedence** **Incorrect: Logical operators without parentheses** ```baba // WRONG - Logical operators need parentheses a > 0 and b > 0; // Error: Unexpected token: KEYWORD (and) x = y or y = z; // Error: Unexpected token: KEYWORD (or) isValid and hasData; // Error: Unexpected token: KEYWORD (and) ``` **Correct: Logical operators wrapped in parentheses** ```baba // CORRECT - Wrap logical expressions in parentheses (a > 0) and (b > 0); // ✓ Works correctly (x = y) or (y = z); // ✓ Works correctly (isValid) and (hasData); // ✓ Works correctly ``` ### **Critical Rule: Complex Comparison Chains** **Incorrect: Complex comparisons without parentheses** ```baba // WRONG - Complex comparisons need parentheses math.abs (x * x + y * y) - (z * z) < 0.001; // Error: Unexpected token: OPERATOR (-) side1 + side2 > side3; // Error: Unexpected token: OPERATOR (>) ``` **Correct: Complex comparisons wrapped in parentheses** ```baba // CORRECT - Wrap complex comparisons in parentheses (math.abs ((x * x + y * y) - (z * z))) < 0.001; // ✓ Works correctly ((side1 + side2) > side3); // ✓ Works correctly ``` ### **Automation Rules for Future Tooling** For future syntax correction tools, apply these rules in order: #### **Rule 1: Function Call Wrapping** - **Pattern**: `function_name args OPERATOR value` - **Action**: Wrap function call: `(function_name args) OPERATOR value` - **Examples**: - `length list > 0` → `(length list) > 0` - `math.sqrt x < 10` → `(math.sqrt x) < 10` - `str.trim input >= 5` → `(str.trim input) >= 5` #### **Rule 2: Logical Operator Wrapping** - **Pattern**: `expression1 and expression2` or `expression1 or expression2` - **Action**: Wrap each expression: `(expression1) and (expression2)` - **Examples**: - `a > 0 and b > 0` → `(a > 0) and (b > 0)` - `x = y or y = z` → `(x = y) or (y = z)` - `isValid and hasData and isReady` → `(isValid) and (hasData) and (isReady)` #### **Rule 3: Complex Expression Wrapping** - **Pattern**: `arithmetic_expression OPERATOR value` - **Action**: Wrap arithmetic expression: `(arithmetic_expression) OPERATOR value` - **Examples**: - `x + y > z` → `(x + y) > z` - `a * b + c <= d` → `(a * b + c) <= d` - `math.abs (x - y) < 0.001` → `(math.abs (x - y)) < 0.001` #### **Rule 4: Nested Function Call Wrapping** - **Pattern**: `function_call (args) OPERATOR value` - **Action**: Wrap entire function call: `(function_call (args)) OPERATOR value` - **Examples**: - `math.abs (x * x + y * y) < 0.001` → `(math.abs (x * x + y * y)) < 0.001` - `str.length (str.trim input) >= 5` → `(str.length (str.trim input)) >= 5` ### **Common Patterns That Always Need Parentheses** ```baba // These patterns ALWAYS need parentheses: // 1. Function calls in comparisons (length list) > 0; (math.sqrt x) < 10; (str.trim input) >= 5; // 2. Logical combinations (a > 0) and (b > 0); (x = y) or (y = z); (isValid) and (hasData); // 3. Complex arithmetic in comparisons ((a + b) > c); ((x * y + z) <= 100); ((math.abs (x - y)) < 0.001); // 4. Nested function calls in comparisons ((math.abs (x * x + y * y)) < 0.001); ((str.length (str.trim input)) >= 10); ``` ### **Why These Rules Exist** Baba Yaga's parser is designed for clarity and explicit precedence. Unlike languages that use operator precedence rules, Baba Yaga requires explicit parentheses to: 1. **Eliminate ambiguity** - No guessing about operator precedence 2. **Improve readability** - Intent is always clear 3. **Prevent errors** - Compile-time detection of precedence issues 4. **Enable parsing** - Simpler, more predictable parsing logic ### **Regex Patterns for Automated Correction** For future tooling, these regex patterns can identify and fix common parenthesization issues: #### **Pattern 1: Function Calls in Comparisons** ```regex # Find: function_name args OPERATOR value (\w+(?:\.\w+)?(?:\s+[^><=!]+)*)\s*([><=!]=?)\s*([^;,\s]+) # Replace: (function_name args) OPERATOR value ($1) $2 $3 # Examples: # length list > 0 → (length list) > 0 # math.abs x < 0.001 → (math.abs x) < 0.001 # str.trim input >= 5 → (str.trim input) >= 5 ``` #### **Pattern 2: Logical Operators** ```regex # Find: expression1 and expression2 ([^;\s]+)\s+(and|or)\s+([^;\s]+) # Replace: (expression1) and (expression2) ($1) $2 ($3) # Examples: # a > 0 and b > 0 → (a > 0) and (b > 0) # x = y or y = z → (x = y) or (y = z) ``` #### **Pattern 3: Complex Arithmetic in Comparisons** ```regex # Find: arithmetic_expression OPERATOR value ([^;\s]*[\+\-\*\/][^;\s]*)\s*([><=!]=?)\s*([^;,\s]+) # Replace: (arithmetic_expression) OPERATOR value ($1) $2 $3 # Examples: # x + y > z → (x + y) > z # a * b + c <= d → (a * b + c) <= d ``` #### **Pattern 4: Nested Function Calls in Comparisons** ```regex # Find: function_call (args) OPERATOR value (\w+(?:\.\w+)?\s*\([^)]+\))\s*([><=!]=?)\s*([^;,\s]+) # Replace: (function_call (args)) OPERATOR value ($1) $2 $3 # Examples: # math.abs (x - y) < 0.001 → (math.abs (x - y)) < 0.001 # str.length (str.trim input) >= 5 → (str.length (str.trim input)) >= 5 ``` ### **Automated Correction Algorithm** ```python def fix_baba_yaga_syntax(code): """ Apply parenthesization rules to Baba Yaga code. Apply rules in order to avoid conflicts. """ # Rule 1: Fix function calls in comparisons code = re.sub( r'(\w+(?:\.\w+)?(?:\s+[^><=!]+)*)\s*([><=!]=?)\s*([^;,\s]+)', r'(\1) \2 \3', code ) # Rule 2: Fix logical operators code = re.sub( r'([^;\s]+)\s+(and|or)\s+([^;\s]+)', r'(\1) \2 (\3)', code ) # Rule 3: Fix complex arithmetic in comparisons code = re.sub( r'([^;\s]*[\+\-\*\/][^;\s]*)\s*([><=!]=?)\s*([^;,\s]+)', r'(\1) \2 \3', code ) # Rule 4: Fix nested function calls in comparisons code = re.sub( r'(\w+(?:\.\w+)?\s*\([^)]+\))\s*([><=!]=?)\s*([^;,\s]+)', r'(\1) \2 \3', code ) return code ``` ### **Validation Rules** After applying corrections, validate that: 1. **All comparisons have balanced parentheses** 2. **Logical operators are properly wrapped** 3. **Function calls in comparisons are wrapped** 4. **No syntax errors remain** ### **Edge Cases and Limitations** - **Nested parentheses**: May require multiple passes - **Complex expressions**: May need manual review - **String literals**: Avoid modifying content inside quotes - **Comments**: Preserve comment formatting - **Line breaks**: Handle multi-line expressions carefully ## Type System Gotchas ### **Incorrect: Expecting JavaScript-like type behavior** ```baba // WRONG - Baba Yaga has different type semantics x : null; // No null value x : undefined; // No undefined x : NaN; // No NaN ``` ### **Correct: Use Baba Yaga type system** ```baba // CORRECT - Use Result type for optional values x : Ok 5; // Success case x : Err "error"; // Error case // CORRECT - Use when expressions for conditional logic x : when (y > 0) is true then y _ then 0; ``` ### **Incorrect: Ignoring type widening rules** ```baba // WRONG - May cause type errors floatVal Float; floatVal : 3.14; // Float literal intVal Int; intVal : floatVal; // Error: Float cannot be assigned to Int ``` ### **Correct: Follow type widening hierarchy** ```baba // CORRECT - Int ⊂ Float ⊂ Number intVal Int; intVal : 5; floatVal Float; floatVal : intVal; // Int → Float ✓ numberVal Number; numberVal : floatVal; // Float → Number ✓ ``` ## Data Structure Syntax ### **Incorrect: Table literal shorthand** ```baba // WRONG - Baba Yaga requires key: value pairs { sum, product, difference } // Missing colons ``` ### **Correct: Explicit key-value pairs** ```baba // CORRECT - Explicit key: value syntax { sum: sum, product: product, difference: difference } ``` ### **Incorrect: Dynamic property access** ```baba // WRONG - Dynamic indexing not supported items.(count - 1) // Unsupported property access ``` ### **Correct: Use when expressions for conditional access** ```baba // CORRECT - Use when for conditional access with ( item : when count is 1 then items.0 _ then when count is 2 then items.1 _ then items.2; ) -> item; ``` ### **Incorrect: Expecting JavaScript-like object access** ```baba // WRONG - Baba Yaga objects have different structure result.property; // May not work as expected result['property']; // Not supported ``` ### **Correct: Use Baba Yaga object access patterns** ```baba // CORRECT - Baba Yaga objects use properties Map result.properties.get('property'); // CORRECT - For table literals, use dot notation { name: "John", age: 30 }.name; // "John" ``` ## Cross-References For comprehensive information about Baba Yaga's type system, see: - **[Types Documentation](./04_types.md)** - Complete type system reference - **[Recursion Documentation](./05_recursion-and-composition.md)** - Details on `with rec` usage - **[Crash Course](./00_crash-course.md)** - Examples and patterns - **[JavaScript Interop](./09_js-interop.md)** - Complete JS interop reference ## Common Error Patterns ### 1. **Unexpected SEMICOLON errors** - **Cause**: Missing semicolon after `when` expressions in `with` blocks - **Solution**: Always add semicolon after each `with` block entry ### 2. **Unexpected COLON errors** - **Cause**: Incorrect table literal syntax - **Solution**: Use `{ key: value }` not `{ key, value }` ### 3. **Unexpected KEYWORD errors** - **Cause**: Incorrect when expression structure - **Solution**: Use `_ then when (condition) is` pattern ### 4. **Unexpected RBRACKET errors** - **Cause**: Complex expressions in list literals - **Solution**: Pre-compute values before creating lists ### 5. **"with rec expects function-valued bindings" errors** - **Cause**: Non-function bindings in with rec - **Solution**: Only use function bindings in with rec ### 6. **"Undefined property" errors** - **Cause**: Using non-existent functions - **Solution**: Check documentation for available functions ## Best Practices ### 1. **When Expressions** - Always use `_ then` for fallback cases - Use `then when` for nested conditions - Wrap complex conditions in parentheses - Add semicolons after when expressions in `with` blocks ### 2. **With vs With Rec** - **Use `with`** for: - Simple local bindings - Computed values - Mixed types (functions, values, expressions) - Non-recursive local functions - **Use `with rec`** for: - Mutually recursive functions only - When local functions need to reference each other - Never for non-function bindings ### 3. **With Blocks** - Add semicolons after each entry - Use with rec only for mutually recursive functions - Pre-compute complex values before using in data structures ### 4. **Type System** - Use simple type declarations: `x Int; x : 5;` - Avoid function type annotations ### 5. **Data Structures** - Use explicit `key: value` pairs in tables - Use `when` expressions for conditional access - Avoid dynamic property access ## Debugging Tips ### 1. **Check Semicolons** - Ensure every `with` block entry ends `with` semicolon - Check `when` expressions in `with` blocks ### 2. **Verify When Structure** - Use `_ then when (condition) is` pattern - Avoid `else` keyword - Wrap complex conditions in parentheses ### 3. **Validate Functions** - Check function names against documentation - Ensure `with rec` only has function bindings - Verify function syntax `with` arrows and semicolons ### 4. **Check Data Structure Syntax** - Use explicit `key: value` pairs ### 5. **Check Type System Usage** - Follow type widening rules: `Int` → `Float` → `Number` - Use `Result` type for optional values ### 6. **Check Operator Precedence and Parentheses** - Wrap function calls in comparisons: `(length list) > 0` - Wrap logical expressions: `(a > 0) and (b > 0)` - Wrap complex arithmetic: `(x + y) > z` - See [Operator Precedence Rules](#operator-precedence-and-parenthesization-rules) for details ## JavaScript Interop Gotchas When working with JavaScript interop, there are some specific gotchas to be aware of: ### **JSValue Wrapper Behavior** ```baba // WRONG - Expecting direct value access result : io.callJS "Math.abs" [-42]; value : result.value; // This is a JSValue wrapper, not the raw number // CORRECT - Pass JSValue directly to other io.* functions result : io.callJS "Math.abs" [-42]; when result is Ok jsValue then io.objectToTable jsValue // JSValue accepted directly Err msg then Err msg; ``` ### **Type Conversion Timing** ```baba // WRONG - Premature conversion can lose JS semantics parsed : io.callJS "JSON.parse" [jsonString]; table : when parsed is Ok jsValue then io.objectToTable jsValue; // Converts immediately // CORRECT - Keep as JSValue until needed parsed : io.callJS "JSON.parse" [jsonString]; // Work with JSValue directly, convert only when needed ``` For comprehensive JavaScript interop documentation, see [JavaScript Interop](./09_js-interop.md).