about summary refs log tree commit diff stats
path: root/js/scripting-lang/parser.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/scripting-lang/parser.js')
-rw-r--r--js/scripting-lang/parser.js661
1 files changed, 568 insertions, 93 deletions
diff --git a/js/scripting-lang/parser.js b/js/scripting-lang/parser.js
index b1aa77f..32837f7 100644
--- a/js/scripting-lang/parser.js
+++ b/js/scripting-lang/parser.js
@@ -5,7 +5,7 @@
 import { TokenType } from './lexer.js';
 
 /**
- * Parser: Converts tokens to an Abstract Syntax Tree (AST).
+ * Parser: Converts tokens to an Abstract Syntax Tree (AST) using combinator-based architecture.
  * 
  * @param {Array.<Object>} tokens - Array of tokens from the lexer
  * @returns {Object} Abstract Syntax Tree with program body
@@ -26,10 +26,19 @@ import { TokenType } from './lexer.js';
  * - Function calls are detected by looking for identifiers followed by expressions
  * - When expressions and case patterns are parsed with special handling
  * - Table literals and access are parsed as structured data
+ * - Function composition uses 'via' keyword with right-associative precedence
+ * - Function application uses juxtaposition with left-associative precedence
  * 
  * The parser maintains a current token index and advances through the token
  * stream, building the AST bottom-up from primary expressions to complex
- * logical expressions.
+ * logical expressions. This approach ensures that all operations are consistently
+ * represented as function calls, enabling the interpreter to use the combinator
+ * foundation for execution.
+ * 
+ * This design choice eliminates the need for special operator handling in the
+ * interpreter and enables powerful abstractions through the combinator foundation.
+ * All operations become function calls, providing a consistent and extensible
+ * execution model that can be enhanced by adding new combinator functions.
  */
 export function parser(tokens) {
     let current = 0;
@@ -40,6 +49,20 @@ export function parser(tokens) {
      * @returns {Object} Complete AST with program body
      * @description Iterates through all tokens, parsing each statement or expression
      * and building the program body. Handles empty programs gracefully.
+     * 
+     * This function orchestrates the parsing process by repeatedly calling walk()
+     * until all tokens are consumed. It ensures that the final AST contains all
+     * statements and expressions in the correct order, ready for interpretation
+     * by the combinator-based interpreter.
+     * 
+     * The function implements the top-level parsing strategy by processing each
+     * statement or expression in sequence. This approach enables the parser to
+     * handle complex programs with multiple statements while maintaining the
+     * combinator-based architecture where all operations become function calls.
+     * 
+     * Each call to walk() processes one complete statement or expression, ensuring
+     * that the parser can handle programs of any complexity while maintaining
+     * clear separation between different language constructs.
      */
     function parse() {
         const body = [];
@@ -68,6 +91,19 @@ export function parser(tokens) {
      * 3. When expressions (pattern matching)
      * 4. Function definitions (explicit function declarations)
      * 5. Logical expressions (default case for all other expressions)
+     * 
+     * This function implements the top-level parsing strategy by checking for
+     * specific token patterns that indicate different language constructs.
+     * The order of checks is crucial for correct parsing precedence and
+     * ensures that complex expressions are properly decomposed into their
+     * constituent parts for combinator translation.
+     * 
+     * The function uses a pattern-matching approach to identify language constructs
+     * based on token sequences. This design enables the parser to handle complex
+     * syntax while maintaining clear separation between different constructs.
+     * Each parsing function is responsible for handling its specific syntax
+     * and translating it into appropriate AST nodes for the combinator-based
+     * interpreter.
      */
     function walk() {
         const token = tokens[current];
@@ -224,6 +260,9 @@ export function parser(tokens) {
      * AST that the interpreter can efficiently evaluate.
      */
     function parseWhenExpression() {
+        if (process.env.DEBUG) {
+            console.log(`[DEBUG] parseWhenExpression: starting, current token = ${tokens[current].type}`);
+        }
         current++; // Skip 'when'
         
         // Parse the value(s) - can be single value or multiple values
@@ -233,23 +272,9 @@ export function parser(tokens) {
             // but not treat them as function calls
             let value;
             if (tokens[current].type === TokenType.IDENTIFIER) {
-                // Check if this is followed by another identifier (multi-value case)
-                if (current + 1 < tokens.length && 
-                    tokens[current + 1].type === TokenType.IDENTIFIER && 
-                    tokens[current + 2].type === TokenType.IS) {
-                    // This is a multi-value case like "when x y is"
-                    value = { type: 'Identifier', value: tokens[current].value };
-                    current++;
-                    values.push(value);
-                    value = { type: 'Identifier', value: tokens[current].value };
-                    current++;
-                    values.push(value);
-                    break; // We've consumed both values and will hit IS next
-                } else {
-                    // Single identifier value
-                    value = { type: 'Identifier', value: tokens[current].value };
-                    current++;
-                }
+                // Single identifier value
+                value = { type: 'Identifier', value: tokens[current].value };
+                current++;
             } else {
                 // For other types, use normal expression parsing
                 value = parseLogicalExpression();
@@ -265,6 +290,9 @@ export function parser(tokens) {
         const cases = [];
         
         while (current < tokens.length) {
+            if (process.env.DEBUG) {
+                console.log(`[DEBUG] parseWhenExpression: starting new case, current token = ${tokens[current].type}, value = ${tokens[current].value || 'N/A'}`);
+            }
             // Parse pattern(s) - can be single pattern or multiple patterns
             const patterns = [];
             
@@ -274,7 +302,19 @@ export function parser(tokens) {
                 if (process.env.DEBUG) {
                     console.log(`[DEBUG] parseWhenExpression: parsing pattern, current token = ${tokens[current].type}, value = ${tokens[current].value || 'N/A'}`);
                 }
-                if (tokens[current].type === TokenType.IDENTIFIER) {
+                
+                // Check if this is a comparison expression (starts with identifier followed by comparison operator)
+                if (tokens[current].type === TokenType.IDENTIFIER && 
+                    current + 1 < tokens.length &&
+                    (tokens[current + 1].type === TokenType.LESS_THAN ||
+                     tokens[current + 1].type === TokenType.GREATER_THAN ||
+                     tokens[current + 1].type === TokenType.LESS_EQUAL ||
+                     tokens[current + 1].type === TokenType.GREATER_EQUAL ||
+                     tokens[current + 1].type === TokenType.EQUALS ||
+                     tokens[current + 1].type === TokenType.NOT_EQUAL)) {
+                    // Parse as a comparison expression
+                    pattern = parseExpression();
+                } else if (tokens[current].type === TokenType.IDENTIFIER) {
                     pattern = { type: 'Identifier', value: tokens[current].value };
                     current++;
                 } else if (tokens[current].type === TokenType.NUMBER) {
@@ -289,10 +329,25 @@ export function parser(tokens) {
                 } else if (tokens[current].type === TokenType.FUNCTION_REF) {
                     pattern = { type: 'FunctionReference', name: tokens[current].name };
                     current++;
+                } else if (tokens[current].type === TokenType.TRUE) {
+                    pattern = { type: 'BooleanLiteral', value: true };
+                    current++;
+                } else if (tokens[current].type === TokenType.FALSE) {
+                    pattern = { type: 'BooleanLiteral', value: false };
+                    current++;
                 } else {
-                    throw new Error(`Expected pattern (identifier, number, string, wildcard, or function reference) in when expression, got ${tokens[current].type}`);
+                    throw new Error(`Expected pattern (identifier, number, string, wildcard, function reference, boolean, or comparison) in when expression, got ${tokens[current].type}`);
                 }
                 patterns.push(pattern);
+                
+                // If we have multiple patterns, we need to handle them correctly
+                // Check if the next token is a valid pattern start (not THEN)
+                if (current < tokens.length && 
+                    tokens[current].type !== TokenType.THEN &&
+                    tokens[current].type !== TokenType.SEMICOLON) {
+                    // Continue parsing more patterns
+                    continue;
+                }
             }
             
             if (current >= tokens.length || tokens[current].type !== TokenType.THEN) {
@@ -300,28 +355,121 @@ export function parser(tokens) {
             }
             current++; // Skip 'then'
             
-            // Parse result
-            const result = parseLogicalExpression();
+            // Parse result - be careful not to parse beyond the result
+            let result;
+            
+            // Check if the next token after THEN is a pattern start
+            if (current < tokens.length) {
+                const nextToken = tokens[current];
+                if (nextToken.type === TokenType.IDENTIFIER ||
+                    nextToken.type === TokenType.NUMBER ||
+                    nextToken.type === TokenType.STRING ||
+                    nextToken.type === TokenType.WILDCARD ||
+                    nextToken.type === TokenType.FUNCTION_REF) {
+                    // Look ahead to see if this is actually a pattern
+                    let lookAhead = current;
+                    while (lookAhead < tokens.length && 
+                           tokens[lookAhead].type !== TokenType.THEN &&
+                           tokens[lookAhead].type !== TokenType.SEMICOLON) {
+                        lookAhead++;
+                    }
+                    
+                    if (lookAhead < tokens.length && tokens[lookAhead].type === TokenType.THEN) {
+                        // This is a pattern start, so the result is just the current token
+                        if (nextToken.type === TokenType.IDENTIFIER) {
+                            result = { type: 'Identifier', value: nextToken.value };
+                        } else if (nextToken.type === TokenType.NUMBER) {
+                            result = { type: 'NumberLiteral', value: nextToken.value };
+                        } else if (nextToken.type === TokenType.STRING) {
+                            result = { type: 'StringLiteral', value: nextToken.value };
+                        } else if (nextToken.type === TokenType.WILDCARD) {
+                            result = { type: 'WildcardPattern' };
+                        } else if (nextToken.type === TokenType.FUNCTION_REF) {
+                            result = { type: 'FunctionReference', name: nextToken.name };
+                        }
+                        current++; // Consume the token
+                    } else {
+                        // This is part of the result, parse normally
+                        result = parseLogicalExpression();
+                    }
+                } else if (nextToken.type === TokenType.WHEN) {
+                    // This is a nested when expression, parse it directly
+                    result = parseWhenExpression();
+                } else {
+                    // Not a pattern start, parse normally
+                    result = parseLogicalExpression();
+                }
+            } else {
+                result = parseLogicalExpression();
+            }
             
             cases.push({
                 pattern: patterns,
                 result: [result]
             });
             
-            // Stop parsing cases when we hit a semicolon
-            if (current < tokens.length && tokens[current].type === TokenType.SEMICOLON) {
-                current++;
-                break;
-            } else {
-                // No semicolon, but check if next token is a valid pattern
-                if (
-                    current >= tokens.length ||
-                    (tokens[current].type !== TokenType.IDENTIFIER &&
-                     tokens[current].type !== TokenType.NUMBER &&
-                     tokens[current].type !== TokenType.STRING &&
-                     tokens[current].type !== TokenType.WILDCARD &&
-                     tokens[current].type !== TokenType.FUNCTION_REF)
-                ) {
+            if (process.env.DEBUG) {
+                console.log(`[DEBUG] parseWhenExpression: finished case, current token = ${tokens[current].type}, value = ${tokens[current].value || 'N/A'}`);
+            }
+            
+            // Enhanced termination logic for when expressions
+            if (current < tokens.length) {
+                const nextToken = tokens[current];
+                
+                if (process.env.DEBUG) {
+                    console.log(`[DEBUG] parseWhenExpression: checking termination, nextToken = ${nextToken.type}, value = ${nextToken.value || 'N/A'}`);
+                }
+                
+                // Stop on semicolon
+                if (nextToken.type === TokenType.SEMICOLON) {
+                    if (process.env.DEBUG) {
+                        console.log(`[DEBUG] parseWhenExpression: terminating on SEMICOLON`);
+                    }
+                    current++;
+                    break;
+                }
+                
+                // Stop on assignment (for consecutive assignments)
+                if (nextToken.type === TokenType.ASSIGNMENT) {
+                    if (process.env.DEBUG) {
+                        console.log(`[DEBUG] parseWhenExpression: terminating on ASSIGNMENT`);
+                    }
+                    break;
+                }
+                
+                // Stop on identifier that starts a new assignment
+                if (nextToken.type === TokenType.IDENTIFIER) {
+                    // Look ahead to see if this is the start of a new assignment
+                    let lookAhead = current;
+                    while (lookAhead < tokens.length && 
+                           tokens[lookAhead].type !== TokenType.ASSIGNMENT &&
+                           tokens[lookAhead].type !== TokenType.SEMICOLON &&
+                           tokens[lookAhead].type !== TokenType.THEN) {
+                        lookAhead++;
+                    }
+                    
+                    if (lookAhead < tokens.length && tokens[lookAhead].type === TokenType.ASSIGNMENT) {
+                        // This is the start of a new assignment, terminate the when expression
+                        if (process.env.DEBUG) {
+                            console.log(`[DEBUG] parseWhenExpression: terminating on new assignment starting with ${nextToken.value}`);
+                        }
+                        break;
+                    }
+                }
+                
+                // Stop on right brace (for when expressions inside table literals)
+                if (nextToken.type === TokenType.RIGHT_BRACE) {
+                    if (process.env.DEBUG) {
+                        console.log(`[DEBUG] parseWhenExpression: terminating on RIGHT_BRACE`);
+                    }
+                    break;
+                }
+                
+                // Stop on comma (for when expressions inside table literals)
+                if (nextToken.type === TokenType.COMMA) {
+                    if (process.env.DEBUG) {
+                        console.log(`[DEBUG] parseWhenExpression: terminating on COMMA`);
+                    }
                     break;
                 }
             }
@@ -334,6 +482,8 @@ export function parser(tokens) {
         };
     }
     
+
+
     /**
      * Parse function definitions: function (params) : body
      * 
@@ -506,21 +656,51 @@ export function parser(tokens) {
      * executed by the interpreter using standard library combinators.
      */
     function parseExpression() {
+        if (process.env.DEBUG) {
+            console.log(`[DEBUG] parseExpression: starting, current token = ${tokens[current].type}`);
+        }
+        
+        // Handle unary minus at the beginning of expressions
+        if (current < tokens.length && tokens[current].type === TokenType.MINUS) {
+            if (process.env.DEBUG) {
+                console.log(`[DEBUG] parseExpression: handling unary minus`);
+            }
+            current++;
+            const operand = parseTerm();
+            return {
+                type: 'FunctionCall',
+                name: 'negate',
+                args: [operand]
+            };
+        }
+        
         let left = parseTerm();
         
+        if (process.env.DEBUG) {
+            console.log(`[DEBUG] parseExpression: after parseTerm, current token = ${tokens[current].type}`);
+        }
+        
         while (current < tokens.length) {
             const token = tokens[current];
             
             if (process.env.DEBUG) {
-                console.log(`[DEBUG] parseExpression: current token = ${token.type}, value = ${token.value || 'N/A'}`);
+                console.log(`[DEBUG] parseExpression: while loop, current token = ${token.type}, value = ${token.value || 'N/A'}`);
             }
             
-            if (token.type === TokenType.PLUS || token.type === TokenType.MINUS) {
+            if (token.type === TokenType.PLUS) {
                 current++;
                 const right = parseTerm();
                 left = {
                     type: 'FunctionCall',
-                    name: token.type === TokenType.PLUS ? 'add' : 'subtract',
+                    name: 'add',
+                    args: [left, right]
+                };
+            } else if (token.type === TokenType.MINUS) {
+                current++;
+                const right = parseTerm();
+                left = {
+                    type: 'FunctionCall',
+                    name: 'subtract',
                     args: [left, right]
                 };
             } else if (token.type === TokenType.EQUALS || 
@@ -557,7 +737,10 @@ export function parser(tokens) {
      * FunctionCall nodes using the corresponding combinator functions.
      */
     function parseTerm() {
-        let left = parseFactor();
+        if (process.env.DEBUG) {
+            console.log(`[DEBUG] parseTerm: starting, current token = ${tokens[current].type}`);
+        }
+        let left = parseApplication();
         
         while (current < tokens.length) {
             const token = tokens[current];
@@ -590,8 +773,12 @@ export function parser(tokens) {
      * to FunctionCall nodes using the corresponding combinator functions.
      */
     function parseFactor() {
+        if (process.env.DEBUG) {
+            console.log(`[DEBUG] parseFactor: starting, current token = ${tokens[current].type}`);
+        }
         let left = parsePrimary();
         
+        // Parse power expressions (existing logic)
         while (current < tokens.length) {
             const token = tokens[current];
             
@@ -612,6 +799,94 @@ export function parser(tokens) {
     }
     
     /**
+     * Parse function composition expressions
+     * 
+     * @returns {Object} AST node representing the composition expression
+     * @description Parses function composition using the 'via' keyword
+     * with right-associative precedence: f via g via h = compose(f, compose(g, h))
+     * 
+     * Function composition is a fundamental feature that allows functions to be
+     * combined naturally. The right-associative precedence means that composition
+     * chains are built from right to left, which matches mathematical function
+     * composition notation. This enables powerful functional programming patterns
+     * where complex transformations can be built from simple, composable functions.
+     */
+    function parseComposition() {
+        let left = parseFactor();
+        
+        // Parse right-associative composition: f via g via h = compose(f, compose(g, h))
+        while (current < tokens.length && tokens[current].type === TokenType.COMPOSE) {
+            current++; // Skip 'via'
+            const right = parseFactor();
+            
+            left = {
+                type: 'FunctionCall',
+                name: 'compose',
+                args: [left, right]
+            };
+        }
+        
+        return left;
+    }
+    
+    /**
+     * Parse function application (juxtaposition)
+     * 
+     * @returns {Object} AST node representing the function application
+     * @description Parses function application using juxtaposition (f x)
+     * with left-associative precedence: f g x = apply(apply(f, g), x)
+     * 
+     * Function application using juxtaposition is the primary mechanism for
+     * calling functions in the language. The left-associative precedence means
+     * that application chains are built from left to right, which is intuitive
+     * for most programmers. This approach eliminates the need for parentheses
+     * in many cases while maintaining clear precedence rules.
+     */
+    function parseApplication() {
+        let left = parseComposition();
+        
+        // Parse left-associative function application: f g x = apply(apply(f, g), x)
+        while (current < tokens.length && isValidArgumentStart(tokens[current])) {
+            const arg = parseComposition(); // Parse the argument as a composition expression
+            left = {
+                type: 'FunctionCall',
+                name: 'apply',
+                args: [left, arg]
+            };
+        }
+        
+        return left;
+    }
+    
+    /**
+     * Check if a token is a valid start of a function argument
+     * 
+     * @param {Object} token - Token to check
+     * @returns {boolean} True if the token can start a function argument
+     * @description Determines if a token can be the start of a function argument.
+     * This is used to detect function application (juxtaposition) where function
+     * application binds tighter than infix operators.
+     * 
+     * This function is crucial for the juxtaposition-based function application
+     * system. It determines when the parser should treat an expression as a
+     * function argument rather than as part of an infix operator expression.
+     * The tokens that can start arguments are carefully chosen to ensure that
+     * function application has the correct precedence relative to operators.
+     */
+    function isValidArgumentStart(token) {
+        return token.type === TokenType.IDENTIFIER ||
+               token.type === TokenType.NUMBER ||
+               token.type === TokenType.STRING ||
+               token.type === TokenType.LEFT_PAREN ||
+               token.type === TokenType.LEFT_BRACE ||
+               token.type === TokenType.TRUE ||
+               token.type === TokenType.FALSE ||
+               token.type === TokenType.FUNCTION_REF ||
+               token.type === TokenType.FUNCTION_ARG ||
+               token.type === TokenType.NOT;
+    }
+    
+    /**
      * Parse table literals: {key: value, key2: value2} or {value1, value2, value3}
      * 
      * @returns {Object} TableLiteral AST node
@@ -646,14 +921,149 @@ export function parser(tokens) {
                     // This is a key-value pair: key : value
                     key = { type: 'Identifier', value: identifier };
                     current++; // Skip ':'
-                    value = parseLogicalExpression();
+                    
+                    // Check if the value is an arrow function
+                    let isArrowFunction = false;
+                    let lookAhead = current;
+                    
+                    // Look ahead to see if this is an arrow function
+                    while (lookAhead < tokens.length && tokens[lookAhead].type === TokenType.IDENTIFIER) {
+                        lookAhead++;
+                    }
+                    
+                    if (lookAhead < tokens.length && tokens[lookAhead].type === TokenType.ARROW) {
+                        // This is an arrow function
+                        isArrowFunction = true;
+                        
+                        // Parse parameters
+                        const params = [];
+                        while (current < tokens.length && tokens[current].type === TokenType.IDENTIFIER) {
+                            params.push(tokens[current].value);
+                            current++;
+                        }
+                        
+                        if (current >= tokens.length || tokens[current].type !== TokenType.ARROW) {
+                            throw new Error('Expected "->" after parameters in arrow function');
+                        }
+                        current++; // Skip '->'
+                        
+                        // Check if the body is a when expression
+                        let body;
+                        if (tokens[current].type === TokenType.WHEN) {
+                            body = parseWhenExpression();
+                        } else {
+                            body = parseLogicalExpression();
+                        }
+                        
+                        value = {
+                            type: 'FunctionDeclaration',
+                            params,
+                            body
+                        };
+                    } else {
+                        // This is a regular value
+                        value = parseLogicalExpression();
+                    }
                 } else {
                     // This is just a value (array-like entry)
                     value = { type: 'Identifier', value: identifier };
                 }
+            } else if (tokens[current].type === TokenType.NUMBER) {
+                // Could be a numeric key or a value
+                const number = tokens[current].value;
+                current++;
+                
+                if (current < tokens.length && tokens[current].type === TokenType.ASSIGNMENT) {
+                    // This is a key-value pair: number : value
+                    key = { type: 'NumberLiteral', value: number };
+                    current++; // Skip ':'
+                    value = parseLogicalExpression();
+                } else {
+                    // This is just a value (array-like entry)
+                    value = { type: 'NumberLiteral', value: number };
+                }
+            } else if (tokens[current].type === TokenType.TRUE) {
+                // Could be a boolean key or a value
+                current++;
+                
+                if (current < tokens.length && tokens[current].type === TokenType.ASSIGNMENT) {
+                    // This is a key-value pair: true : value
+                    key = { type: 'BooleanLiteral', value: true };
+                    current++; // Skip ':'
+                    value = parseLogicalExpression();
+                } else {
+                    // This is just a value (array-like entry)
+                    value = { type: 'BooleanLiteral', value: true };
+                }
+            } else if (tokens[current].type === TokenType.FALSE) {
+                // Could be a boolean key or a value
+                current++;
+                
+                if (current < tokens.length && tokens[current].type === TokenType.ASSIGNMENT) {
+                    // This is a key-value pair: false : value
+                    key = { type: 'BooleanLiteral', value: false };
+                    current++; // Skip ':'
+                    value = parseLogicalExpression();
+                } else {
+                    // This is just a value (array-like entry)
+                    value = { type: 'BooleanLiteral', value: false };
+                }
+            } else if (tokens[current].type === TokenType.LEFT_PAREN) {
+                // This could be a computed key or a value
+                const expression = parseLogicalExpression();
+                
+                if (current < tokens.length && tokens[current].type === TokenType.ASSIGNMENT) {
+                    // This is a key-value pair: (expression) : value
+                    key = expression;
+                    current++; // Skip ':'
+                    value = parseLogicalExpression();
+                } else {
+                    // This is just a value (array-like entry)
+                    value = expression;
+                }
             } else {
-                // This is a value (array-like entry)
-                value = parseLogicalExpression();
+                // Check if this is an arrow function: param1 param2 -> body
+                let isArrowFunction = false;
+                let lookAhead = current;
+                
+                // Look ahead to see if this is an arrow function
+                while (lookAhead < tokens.length && tokens[lookAhead].type === TokenType.IDENTIFIER) {
+                    lookAhead++;
+                }
+                
+                if (lookAhead < tokens.length && tokens[lookAhead].type === TokenType.ARROW) {
+                    // This is an arrow function
+                    isArrowFunction = true;
+                    
+                    // Parse parameters
+                    const params = [];
+                    while (current < tokens.length && tokens[current].type === TokenType.IDENTIFIER) {
+                        params.push(tokens[current].value);
+                        current++;
+                    }
+                    
+                    if (current >= tokens.length || tokens[current].type !== TokenType.ARROW) {
+                        throw new Error('Expected "->" after parameters in arrow function');
+                    }
+                    current++; // Skip '->'
+                    
+                    // Check if the body is a when expression
+                    let body;
+                    if (tokens[current].type === TokenType.WHEN) {
+                        body = parseWhenExpression();
+                    } else {
+                        body = parseLogicalExpression();
+                    }
+                    
+                    value = {
+                        type: 'FunctionDeclaration',
+                        params,
+                        body
+                    };
+                } else {
+                    // This is a regular value (array-like entry)
+                    value = parseLogicalExpression();
+                }
             }
             
             entries.push({ key, value });
@@ -758,6 +1168,11 @@ export function parser(tokens) {
                 current++;
                 return { type: 'BooleanLiteral', value: false };
                 
+            case TokenType.WHEN:
+                return parseWhenExpression();
+                
+
+                
             case TokenType.IDENTIFIER:
                 const identifierValue = token.value;
                 current++;
@@ -772,11 +1187,47 @@ export function parser(tokens) {
                     }
                     current++; // Skip ']'
                     
-                    return {
+                    let tableNode = {
                         type: 'TableAccess',
                         table: { type: 'Identifier', value: identifierValue },
                         key: keyExpression
                     };
+                    
+                    // Check for chained access: table[key].property or table[key][key2]
+                    while (current < tokens.length && (tokens[current].type === TokenType.DOT || tokens[current].type === TokenType.LEFT_BRACKET)) {
+                        if (tokens[current].type === TokenType.DOT) {
+                            current++; // Skip '.'
+                            
+                            if (current >= tokens.length || tokens[current].type !== TokenType.IDENTIFIER) {
+                                throw new Error('Expected identifier after "." in table access');
+                            }
+                            
+                            const propertyName = tokens[current].value;
+                            current++; // Skip property name
+                            
+                            tableNode = {
+                                type: 'TableAccess',
+                                table: tableNode,
+                                key: { type: 'Identifier', value: propertyName }
+                            };
+                        } else if (tokens[current].type === TokenType.LEFT_BRACKET) {
+                            current++; // Skip '['
+                            const keyExpression2 = parseLogicalExpression();
+                            
+                            if (current >= tokens.length || tokens[current].type !== TokenType.RIGHT_BRACKET) {
+                                throw new Error('Expected "]" after table key');
+                            }
+                            current++; // Skip ']'
+                            
+                            tableNode = {
+                                type: 'TableAccess',
+                                table: tableNode,
+                                key: keyExpression2
+                            };
+                        }
+                    }
+                    
+                    return tableNode;
                 } else if (current < tokens.length && tokens[current].type === TokenType.DOT) {
                     current++; // Skip '.'
                     
@@ -787,52 +1238,57 @@ export function parser(tokens) {
                     const propertyName = tokens[current].value;
                     current++; // Skip property name
                     
-                    return {
+                    let tableNode = {
                         type: 'TableAccess',
                         table: { type: 'Identifier', value: identifierValue },
                         key: { type: 'Identifier', value: propertyName }
                     };
-                }
-                
-                // Parse function call arguments (including parenthesized expressions)
-                const args = [];
-                while (
-                    current < tokens.length &&
-                    (
-                        tokens[current].type === TokenType.IDENTIFIER ||
-                        tokens[current].type === TokenType.NUMBER ||
-                        tokens[current].type === TokenType.STRING ||
-                        tokens[current].type === TokenType.LEFT_PAREN ||
-                        tokens[current].type === TokenType.LEFT_BRACE ||
-                        tokens[current].type === TokenType.TRUE ||
-                        tokens[current].type === TokenType.FALSE ||
-                        tokens[current].type === TokenType.FUNCTION_REF ||
-                        (tokens[current].type === TokenType.MINUS && 
-                         current + 1 < tokens.length && 
-                         tokens[current + 1].type === TokenType.NUMBER)
-                    )
-                ) {
-                    // Special case: if we see FUNCTION_REF followed by MINUS followed by NUMBER,
-                    // parse them as separate arguments
-                    if (tokens[current].type === TokenType.FUNCTION_REF &&
-                        current + 1 < tokens.length && tokens[current + 1].type === TokenType.MINUS &&
-                        current + 2 < tokens.length && tokens[current + 2].type === TokenType.NUMBER) {
-                        // Parse the function reference
-                        args.push(parsePrimary());
-                        // Parse the unary minus as a separate argument
-                        args.push(parsePrimary());
-                    } else {
-                        // Parse each argument as a complete expression
-                        args.push(parseExpression());
+                    
+                    // Check for chained access: table.property[key] or table.property.property2
+                    while (current < tokens.length && (tokens[current].type === TokenType.DOT || tokens[current].type === TokenType.LEFT_BRACKET)) {
+                        if (tokens[current].type === TokenType.DOT) {
+                            current++; // Skip '.'
+                            
+                            if (current >= tokens.length || tokens[current].type !== TokenType.IDENTIFIER) {
+                                throw new Error('Expected identifier after "." in table access');
+                            }
+                            
+                            const propertyName2 = tokens[current].value;
+                            current++; // Skip property name
+                            
+                            tableNode = {
+                                type: 'TableAccess',
+                                table: tableNode,
+                                key: { type: 'Identifier', value: propertyName2 }
+                            };
+                        } else if (tokens[current].type === TokenType.LEFT_BRACKET) {
+                            current++; // Skip '['
+                            const keyExpression = parseLogicalExpression();
+                            
+                            if (current >= tokens.length || tokens[current].type !== TokenType.RIGHT_BRACKET) {
+                                throw new Error('Expected "]" after table key');
+                            }
+                            current++; // Skip ']'
+                            
+                            tableNode = {
+                                type: 'TableAccess',
+                                table: tableNode,
+                                key: keyExpression
+                            };
+                        }
                     }
+                    
+                    return tableNode;
                 }
-                if (args.length > 0) {
-                    return {
-                        type: 'FunctionCall',
-                        name: identifierValue,
-                        args
-                    };
+                
+                // Parenthesized expressions after identifiers are handled by parseApplication
+                // to support function calls like f(x)
+                if (current < tokens.length && tokens[current].type === TokenType.LEFT_PAREN) {
+                    // Don't handle this here, let parseApplication handle it
+                    // This ensures that f(x) is parsed as apply(f, x) not just x
                 }
+                
+                // Juxtaposition function calls are now handled in parseFactor() with proper precedence
                 return { type: 'Identifier', value: identifierValue };
 
             case TokenType.LEFT_PAREN:
@@ -845,6 +1301,16 @@ export function parser(tokens) {
                     throw new Error('Expected ")" after expression');
                 }
                 current++;
+                
+                // Check if this is just a simple identifier in parentheses
+                if (expression.type === 'Identifier') {
+                    return { 
+                        type: 'FunctionCall', 
+                        name: 'identity', 
+                        args: [expression] 
+                    };
+                }
+                
                 return expression;
 
             case TokenType.WILDCARD:
@@ -856,7 +1322,7 @@ export function parser(tokens) {
                 
 
                 
-            case TokenType.NOT:
+                                                case TokenType.NOT:
                 current++;
                 const operand = parsePrimary();
                 return { 
@@ -866,13 +1332,8 @@ export function parser(tokens) {
                 };
                 
             case TokenType.MINUS:
-                current++;
-                const unaryOperand = parsePrimary();
-                return { 
-                    type: 'FunctionCall',
-                    name: 'negate',
-                    args: [unaryOperand]
-                };
+                // Delegate unary minus to parseExpression for proper precedence
+                return parseExpression();
                 
             case TokenType.ARROW:
                 current++;
@@ -880,10 +1341,24 @@ export function parser(tokens) {
                 return { type: 'ArrowExpression', body: arrowBody };
                 
             case TokenType.FUNCTION_REF:
-                const functionRef = { type: 'FunctionReference', name: token.name };
+                const functionRef = { type: 'FunctionReference', name: tokens[current].name };
                 current++;
                 return functionRef;
                 
+            case TokenType.FUNCTION_ARG:
+                // @(expression) - parse the parenthesized expression as a function argument
+                current++; // Skip FUNCTION_ARG token
+                if (current >= tokens.length || tokens[current].type !== TokenType.LEFT_PAREN) {
+                    throw new Error('Expected "(" after @');
+                }
+                current++; // Skip '('
+                const argExpression = parseLogicalExpression();
+                if (current >= tokens.length || tokens[current].type !== TokenType.RIGHT_PAREN) {
+                    throw new Error('Expected ")" after function argument expression');
+                }
+                current++; // Skip ')'
+                return argExpression;
+                
             default:
                 throw new Error(`Unexpected token in parsePrimary: ${token.type}`);
         }