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.js653
1 files changed, 489 insertions, 164 deletions
diff --git a/js/scripting-lang/parser.js b/js/scripting-lang/parser.js
index 055b3c0..01f3648 100644
--- a/js/scripting-lang/parser.js
+++ b/js/scripting-lang/parser.js
@@ -6,15 +6,49 @@ import { TokenType } from './lexer.js';
 
 /**
  * Parser: Converts tokens to an Abstract Syntax Tree (AST).
+ * 
  * @param {Array.<Object>} tokens - Array of tokens from the lexer
  * @returns {Object} Abstract Syntax Tree with program body
  * @throws {Error} For parsing errors like unexpected tokens or missing delimiters
+ * 
+ * @description The parser implements a combinator-based architecture where all
+ * operators are translated to function calls to standard library combinators.
+ * This eliminates parsing ambiguity while preserving the original syntax.
+ * 
+ * The parser uses a recursive descent approach with proper operator precedence
+ * handling. Each operator expression (e.g., x + y) is translated to a FunctionCall
+ * node (e.g., add(x, y)) that will be executed by the interpreter using the
+ * corresponding combinator function.
+ * 
+ * Key architectural decisions:
+ * - All operators become FunctionCall nodes to eliminate ambiguity
+ * - Operator precedence is handled through recursive parsing functions
+ * - 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. This approach ensures that all operations are consistently
+ * represented as function calls, enabling the interpreter to use the combinator
+ * foundation for execution.
  */
 export function parser(tokens) {
     let current = 0;
     
     /**
      * Main parsing function that processes the entire token stream
+     * 
+     * @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.
      */
     function parse() {
         const body = [];
@@ -31,6 +65,24 @@ export function parser(tokens) {
     
     /**
      * Main walk function that dispatches to appropriate parsing functions
+     * 
+     * @returns {Object|null} Parsed AST node or null for empty statements
+     * @description Determines the type of construct at the current position
+     * and delegates to the appropriate parsing function. The order of checks
+     * determines parsing precedence for top-level constructs.
+     * 
+     * Parsing order:
+     * 1. IO operations (highest precedence for top-level constructs)
+     * 2. Assignments (identifier followed by assignment token)
+     * 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.
      */
     function walk() {
         const token = tokens[current];
@@ -65,12 +117,23 @@ export function parser(tokens) {
             return parseFunctionDefinition();
         }
         
+
+        
         // For all other expressions, parse as logical expressions
         return parseLogicalExpression();
     }
     
     /**
      * Parse assignment statements: identifier : expression;
+     * 
+     * @returns {Object} Assignment AST node
+     * @throws {Error} For malformed assignments or missing semicolons
+     * @description Parses variable assignments and function definitions.
+     * Supports both simple assignments (x : 42) and arrow function definitions
+     * (f : x y -> x + y). Also handles when expressions as assignment values.
+     * 
+     * The function uses lookahead to distinguish between different assignment
+     * types and parses the value according to the detected type.
      */
     function parseAssignment() {
         const identifier = tokens[current].value;
@@ -140,103 +203,40 @@ export function parser(tokens) {
                     }
                 };
             } else {
-                // Check if this is a function call: functionName arg1 arg2 ...
-                if (tokens[current].type === TokenType.IDENTIFIER) {
-                    const functionName = tokens[current].value;
-                    current++; // Skip function name
-                    
-                    // Look ahead to see if this is a function call
-                    // A function call should have arguments that are not operators
-                    let lookAhead = current;
-                    let hasArguments = false;
-                    let isFunctionCall = false;
-                    
-                    while (lookAhead < tokens.length && 
-                           lookAhead < current + 5 && // Limit lookahead to avoid infinite loops
-                           tokens[lookAhead].type !== TokenType.SEMICOLON) {
-                        if (tokens[lookAhead].type === TokenType.IDENTIFIER ||
-                            tokens[lookAhead].type === TokenType.NUMBER ||
-                            tokens[lookAhead].type === TokenType.STRING ||
-                            tokens[lookAhead].type === TokenType.TRUE ||
-                            tokens[lookAhead].type === TokenType.FALSE ||
-                            tokens[lookAhead].type === TokenType.LEFT_PAREN) {
-                            hasArguments = true;
-                            isFunctionCall = true;
-                            break;
-                        } else if (tokens[lookAhead].type === TokenType.PLUS ||
-                                   tokens[lookAhead].type === TokenType.MINUS ||
-                                   tokens[lookAhead].type === TokenType.MULTIPLY ||
-                                   tokens[lookAhead].type === TokenType.DIVIDE ||
-                                   tokens[lookAhead].type === TokenType.MODULO ||
-                                   tokens[lookAhead].type === TokenType.POWER ||
-                                   tokens[lookAhead].type === TokenType.EQUALS ||
-                                   tokens[lookAhead].type === TokenType.NOT_EQUAL ||
-                                   tokens[lookAhead].type === TokenType.LESS_THAN ||
-                                   tokens[lookAhead].type === TokenType.GREATER_THAN ||
-                                   tokens[lookAhead].type === TokenType.LESS_EQUAL ||
-                                   tokens[lookAhead].type === TokenType.GREATER_EQUAL ||
-                                   tokens[lookAhead].type === TokenType.AND ||
-                                   tokens[lookAhead].type === TokenType.OR ||
-                                   tokens[lookAhead].type === TokenType.XOR ||
-                                   tokens[lookAhead].type === TokenType.ARROW) {
-                            // This is an expression, not a function call
-                            isFunctionCall = false;
-                            break;
-                        }
-                        lookAhead++;
-                    }
-                    
-                    if (isFunctionCall && hasArguments) {
-                        const value = parseFunctionCall(functionName);
-                        
-                        // Expect semicolon
-                        if (current < tokens.length && tokens[current].type === TokenType.SEMICOLON) {
-                            current++;
-                        }
-                        
-                        return {
-                            type: 'Assignment',
-                            identifier,
-                            value
-                        };
-                    } else {
-                        // Not a function call, parse as expression
-                        // Reset current position and parse the entire value as expression
-                        current = current - 1; // Go back to the identifier
-                        const value = parseLogicalExpression();
-                        
-                        // Expect semicolon
-                        if (current < tokens.length && tokens[current].type === TokenType.SEMICOLON) {
-                            current++;
-                        }
-                        
-                        return {
-                            type: 'Assignment',
-                            identifier,
-                            value
-                        };
-                    }
-                } else {
-                    // Regular expression
-                    const value = parseLogicalExpression();
-                    
-                    // Expect semicolon
-                    if (current < tokens.length && tokens[current].type === TokenType.SEMICOLON) {
-                        current++;
-                    }
-                    
-                    return {
-                        type: 'Assignment',
-                        identifier,
-                        value
-                    };
+                // Parse the value as an expression (function calls will be handled by expression parsing)
+                const value = parseLogicalExpression();
+                
+                // Expect semicolon
+                if (current < tokens.length && tokens[current].type === TokenType.SEMICOLON) {
+                    current++;
                 }
+                
+                return {
+                    type: 'Assignment',
+                    identifier,
+                    value
+                };
             }
         }
     }
     
     /**
      * Parse when expressions: when value is pattern then result pattern then result;
+     * 
+     * @returns {Object} WhenExpression AST node
+     * @throws {Error} For malformed when expressions
+     * @description Parses pattern matching expressions with support for single
+     * and multiple values/patterns. The when expression is the primary pattern
+     * matching construct in the language.
+     * 
+     * Supports:
+     * - Single value patterns: when x is 42 then "correct" _ then "wrong"
+     * - Multiple value patterns: when x y is 0 0 then "both zero" _ _ then "not both"
+     * - Wildcard patterns: _ (matches any value)
+     * - Function references: @functionName
+     * 
+     * The function parses values, patterns, and results, building a structured
+     * AST that the interpreter can efficiently evaluate.
      */
     function parseWhenExpression() {
         current++; // Skip 'when'
@@ -244,7 +244,17 @@ export function parser(tokens) {
         // Parse the value(s) - can be single value or multiple values
         const values = [];
         while (current < tokens.length && tokens[current].type !== TokenType.IS) {
-            const value = parseLogicalExpression();
+            // For when expressions, we want to parse simple identifiers and expressions
+            // but not treat them as function calls
+            let value;
+            if (tokens[current].type === TokenType.IDENTIFIER) {
+                // Single identifier value
+                value = { type: 'Identifier', value: tokens[current].value };
+                current++;
+            } else {
+                // For other types, use normal expression parsing
+                value = parseLogicalExpression();
+            }
             values.push(value);
         }
         
@@ -262,19 +272,55 @@ export function parser(tokens) {
             // Parse patterns until we hit THEN
             while (current < tokens.length && tokens[current].type !== TokenType.THEN) {
                 let pattern;
-                if (tokens[current].type === TokenType.IDENTIFIER) {
+                if (process.env.DEBUG) {
+                    console.log(`[DEBUG] parseWhenExpression: parsing pattern, current token = ${tokens[current].type}, value = ${tokens[current].value || 'N/A'}`);
+                }
+                
+                // 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) {
                     pattern = { type: 'NumberLiteral', value: tokens[current].value };
                     current++;
+                } else if (tokens[current].type === TokenType.STRING) {
+                    pattern = { type: 'StringLiteral', value: tokens[current].value };
+                    current++;
                 } else if (tokens[current].type === TokenType.WILDCARD) {
                     pattern = { type: 'WildcardPattern' };
                     current++;
+                } 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, or wildcard) in when expression');
+                    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,7 +346,9 @@ export function parser(tokens) {
                     current >= tokens.length ||
                     (tokens[current].type !== TokenType.IDENTIFIER &&
                      tokens[current].type !== TokenType.NUMBER &&
-                     tokens[current].type !== TokenType.WILDCARD)
+                     tokens[current].type !== TokenType.STRING &&
+                     tokens[current].type !== TokenType.WILDCARD &&
+                     tokens[current].type !== TokenType.FUNCTION_REF)
                 ) {
                     break;
                 }
@@ -316,6 +364,18 @@ export function parser(tokens) {
     
     /**
      * Parse function definitions: function (params) : body
+     * 
+     * @returns {Object} FunctionDefinition AST node
+     * @throws {Error} For malformed function definitions
+     * @description Parses explicit function declarations with parameter lists
+     * and function bodies. This is the traditional function definition syntax
+     * as opposed to arrow functions.
+     * 
+     * The function expects:
+     * - function keyword
+     * - Parenthesized parameter list
+     * - Assignment token (:)
+     * - Function body expression
      */
     function parseFunctionDefinition() {
         current++; // Skip 'function'
@@ -360,6 +420,11 @@ export function parser(tokens) {
     
     /**
      * Parse IO input operations: ..in
+     * 
+     * @returns {Object} IOInExpression AST node
+     * @description Parses input operations that read from standard input.
+     * The operation is represented as a simple AST node that the interpreter
+     * will handle by prompting for user input.
      */
     function parseIOIn() {
         current++; // Skip IO_IN token
@@ -368,6 +433,12 @@ export function parser(tokens) {
     
     /**
      * Parse IO output operations: ..out expression
+     * 
+     * @returns {Object} IOOutExpression AST node
+     * @throws {Error} For malformed output expressions
+     * @description Parses output operations that write to standard output.
+     * The expression to output is parsed as a logical expression and will
+     * be evaluated by the interpreter before being printed.
      */
     function parseIOOut() {
         current++; // Skip IO_OUT token
@@ -386,6 +457,12 @@ export function parser(tokens) {
     
     /**
      * Parse IO assert operations: ..assert expression
+     * 
+     * @returns {Object} IOAssertExpression AST node
+     * @throws {Error} For malformed assert expressions
+     * @description Parses assertion operations that verify conditions.
+     * The expression is parsed as a logical expression and will be evaluated
+     * by the interpreter. If the result is falsy, an assertion error is thrown.
      */
     function parseIOAssert() {
         current++; // Skip IO_ASSERT token
@@ -404,6 +481,20 @@ export function parser(tokens) {
     
     /**
      * Parse logical expressions with proper precedence
+     * 
+     * @returns {Object} AST node representing the logical expression
+     * @description Parses logical expressions (and, or, xor) with the lowest
+     * precedence. All logical operators are translated to FunctionCall nodes
+     * using the corresponding combinator functions.
+     * 
+     * Operator precedence (lowest to highest):
+     * 1. Logical operators (and, or, xor)
+     * 2. Comparison operators (=, !=, <, >, <=, >=)
+     * 3. Additive operators (+, -)
+     * 4. Multiplicative operators (*, /, %)
+     * 5. Power operator (^)
+     * 6. Unary operators (not, -)
+     * 7. Primary expressions (literals, identifiers, function calls, parentheses)
      */
     function parseLogicalExpression() {
         let left = parseExpression();
@@ -417,10 +508,10 @@ export function parser(tokens) {
                 current++;
                 const right = parseExpression();
                 left = {
-                    type: token.type === TokenType.AND ? 'AndExpression' :
-                          token.type === TokenType.OR ? 'OrExpression' : 'XorExpression',
-                    left,
-                    right
+                    type: 'FunctionCall',
+                    name: token.type === TokenType.AND ? 'logicalAnd' :
+                          token.type === TokenType.OR ? 'logicalOr' : 'logicalXor',
+                    args: [left, right]
                 };
             } else {
                 break;
@@ -432,20 +523,52 @@ export function parser(tokens) {
     
     /**
      * Parse comparison expressions
+     * 
+     * @returns {Object} AST node representing the comparison expression
+     * @description Parses comparison expressions (=, !=, <, >, <=, >=) and
+     * additive expressions (+, -). All operators are translated to FunctionCall
+     * nodes using the corresponding combinator functions.
+     * 
+     * This function implements the core of the combinator-based architecture
+     * by translating operator expressions to function calls that will be
+     * executed by the interpreter using standard library combinators.
      */
     function parseExpression() {
+        // Handle unary minus at the beginning of expressions
+        if (current < tokens.length && tokens[current].type === TokenType.MINUS) {
+            current++;
+            const operand = parseTerm();
+            return {
+                type: 'FunctionCall',
+                name: 'negate',
+                args: [operand]
+            };
+        }
+        
         let left = parseTerm();
         
         while (current < tokens.length) {
             const token = tokens[current];
             
-            if (token.type === TokenType.PLUS || token.type === TokenType.MINUS) {
+            if (process.env.DEBUG) {
+                console.log(`[DEBUG] parseExpression: current token = ${token.type}, value = ${token.value || 'N/A'}`);
+            }
+            
+            if (token.type === TokenType.PLUS) {
+                current++;
+                const right = parseTerm();
+                left = {
+                    type: 'FunctionCall',
+                    name: 'add',
+                    args: [left, right]
+                };
+            } else if (token.type === TokenType.MINUS) {
                 current++;
                 const right = parseTerm();
                 left = {
-                    type: token.type === TokenType.PLUS ? 'PlusExpression' : 'MinusExpression',
-                    left,
-                    right
+                    type: 'FunctionCall',
+                    name: 'subtract',
+                    args: [left, right]
                 };
             } else if (token.type === TokenType.EQUALS || 
                        token.type === TokenType.NOT_EQUAL ||
@@ -456,13 +579,13 @@ export function parser(tokens) {
                 current++;
                 const right = parseTerm();
                 left = {
-                    type: token.type === TokenType.EQUALS ? 'EqualsExpression' :
-                          token.type === TokenType.NOT_EQUAL ? 'NotEqualExpression' :
-                          token.type === TokenType.LESS_THAN ? 'LessThanExpression' :
-                          token.type === TokenType.GREATER_THAN ? 'GreaterThanExpression' :
-                          token.type === TokenType.LESS_EQUAL ? 'LessEqualExpression' : 'GreaterEqualExpression',
-                    left,
-                    right
+                    type: 'FunctionCall',
+                    name: token.type === TokenType.EQUALS ? 'equals' :
+                          token.type === TokenType.NOT_EQUAL ? 'notEquals' :
+                          token.type === TokenType.LESS_THAN ? 'lessThan' :
+                          token.type === TokenType.GREATER_THAN ? 'greaterThan' :
+                          token.type === TokenType.LESS_EQUAL ? 'lessEqual' : 'greaterEqual',
+                    args: [left, right]
                 };
             } else {
                 break;
@@ -474,9 +597,14 @@ export function parser(tokens) {
     
     /**
      * Parse multiplication and division expressions
+     * 
+     * @returns {Object} AST node representing the multiplicative expression
+     * @description Parses multiplicative expressions (*, /, %) with higher
+     * precedence than additive expressions. All operators are translated to
+     * FunctionCall nodes using the corresponding combinator functions.
      */
     function parseTerm() {
-        let left = parseFactor();
+        let left = parseApplication();
         
         while (current < tokens.length) {
             const token = tokens[current];
@@ -487,10 +615,10 @@ export function parser(tokens) {
                 current++;
                 const right = parseFactor();
                 left = {
-                    type: token.type === TokenType.MULTIPLY ? 'MultiplyExpression' :
-                          token.type === TokenType.DIVIDE ? 'DivideExpression' : 'ModuloExpression',
-                    left,
-                    right
+                    type: 'FunctionCall',
+                    name: token.type === TokenType.MULTIPLY ? 'multiply' :
+                          token.type === TokenType.DIVIDE ? 'divide' : 'modulo',
+                    args: [left, right]
                 };
             } else {
                 break;
@@ -502,10 +630,16 @@ export function parser(tokens) {
     
     /**
      * Parse power expressions and unary operators
+     * 
+     * @returns {Object} AST node representing the factor expression
+     * @description Parses power expressions (^) and unary operators (not, -)
+     * with the highest precedence among operators. All operators are translated
+     * to FunctionCall nodes using the corresponding combinator functions.
      */
     function parseFactor() {
         let left = parsePrimary();
         
+        // Parse power expressions (existing logic)
         while (current < tokens.length) {
             const token = tokens[current];
             
@@ -513,9 +647,9 @@ export function parser(tokens) {
                 current++;
                 const right = parsePrimary();
                 left = {
-                    type: 'PowerExpression',
-                    left,
-                    right
+                    type: 'FunctionCall',
+                    name: 'power',
+                    args: [left, right]
                 };
             } else {
                 break;
@@ -526,7 +660,108 @@ 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 ||
+               // Removed: token.type === TokenType.MINUS ||
+               token.type === TokenType.NOT;
+    }
+    
+    /**
      * Parse table literals: {key: value, key2: value2} or {value1, value2, value3}
+     * 
+     * @returns {Object} TableLiteral AST node
+     * @throws {Error} For malformed table literals
+     * @description Parses table literals with support for both key-value pairs
+     * and array-like entries. Tables are the primary data structure in the language.
+     * 
+     * Supports:
+     * - Key-value pairs: {name: "Alice", age: 30}
+     * - Array-like entries: {1, 2, 3}
+     * - Mixed entries: {1, 2, name: "Alice", 3}
+     * 
+     * Array-like entries are automatically assigned numeric keys starting from 1.
      */
     function parseTableLiteral() {
         current++; // Skip '{'
@@ -577,17 +812,27 @@ export function parser(tokens) {
         };
     }
     
+
+    
     /**
      * Parse function calls: functionName arg1 arg2 ...
+     * 
+     * @returns {Object} FunctionCall AST node
+     * @description Parses function calls with multiple arguments. This function
+     * is used by parsePrimary to detect when an identifier is followed by
+     * expressions that should be treated as function arguments.
+     * 
+     * Function calls are detected by the presence of an identifier followed
+     * by expressions that are not operators. The parser uses lookahead to
+     * determine if an identifier should be treated as a function call.
      */
-    function parseFunctionCall(functionName, insideParentheses = false) {
-        const args = [];
+    function parseFunctionCall() {
+        const functionName = tokens[current].value;
+        current++; // Skip function name
         
-        // Parse arguments until we hit a semicolon or right parenthesis (if inside parentheses)
-        while (current < tokens.length && 
-               tokens[current].type !== TokenType.SEMICOLON &&
-               (!insideParentheses || tokens[current].type !== TokenType.RIGHT_PAREN)) {
-            // Parse the argument
+        // Parse arguments until we hit a semicolon or end of tokens
+        const args = [];
+        while (current < tokens.length && tokens[current].type !== TokenType.SEMICOLON) {
             const arg = parseLogicalExpression();
             args.push(arg);
         }
@@ -601,6 +846,26 @@ export function parser(tokens) {
     
     /**
      * Parse primary expressions (literals, identifiers, parenthesized expressions)
+     * 
+     * @returns {Object} AST node representing the primary expression
+     * @throws {Error} For unexpected tokens or malformed expressions
+     * @description Parses the highest precedence expressions including literals,
+     * identifiers, function calls, table access, and parenthesized expressions.
+     * This is the foundation of the expression parsing hierarchy.
+     * 
+     * The function implements sophisticated function call detection by looking
+     * for identifiers followed by expressions that could be arguments. This
+     * approach allows the language to support both traditional function calls
+     * and the ML-style function application syntax.
+     * 
+     * Supports:
+     * - Literals: numbers, strings, booleans
+     * - Identifiers: variables and function names
+     * - Function calls: f(x, y) or f x y
+     * - Table access: table[key] or table.property
+     * - Parenthesized expressions: (x + y)
+     * - Unary operators: not x, -x
+     * - Function references: @functionName
      */
     function parsePrimary() {
         const token = tokens[current];
@@ -609,6 +874,10 @@ export function parser(tokens) {
             throw new Error('Unexpected end of input');
         }
         
+        if (process.env.DEBUG) {
+            console.log(`[DEBUG] parsePrimary: current token = ${token.type}, value = ${token.value || 'N/A'}`);
+        }
+        
         switch (token.type) {
             case TokenType.NUMBER:
                 current++;
@@ -626,67 +895,109 @@ export function parser(tokens) {
                 current++;
                 return { type: 'BooleanLiteral', value: false };
                 
-            case TokenType.IDENTIFIER:
-                current++;
-                return { type: 'Identifier', value: token.value };
+            case TokenType.WHEN:
+                return parseWhenExpression();
                 
-            case TokenType.WILDCARD:
-                current++;
-                return { type: 'WildcardPattern' };
+
                 
-            case TokenType.LEFT_PAREN:
+            case TokenType.IDENTIFIER:
+                const identifierValue = token.value;
                 current++;
                 
-                // Check if this is a function call inside parentheses
-                if (tokens[current].type === TokenType.IDENTIFIER) {
-                    const functionName = tokens[current].value;
-                    current++; // Skip function name
+                // Check for table access: identifier[key] or identifier.property
+                if (current < tokens.length && tokens[current].type === TokenType.LEFT_BRACKET) {
+                    current++; // Skip '['
+                    const keyExpression = parseLogicalExpression();
                     
-                    // Check if there are arguments
-                    if (current < tokens.length && tokens[current].type !== TokenType.RIGHT_PAREN) {
-                        const functionCall = parseFunctionCall(functionName, true);
-                        
-                        if (current >= tokens.length || tokens[current].type !== TokenType.RIGHT_PAREN) {
-                            throw new Error('Expected ")" after function call');
-                        }
-                        current++;
-                        
-                        return functionCall;
-                    } else {
-                        // Just a function name in parentheses
-                        const identifier = { type: 'Identifier', value: functionName };
-                        
-                        if (current >= tokens.length || tokens[current].type !== TokenType.RIGHT_PAREN) {
-                            throw new Error('Expected ")" after identifier');
-                        }
-                        current++;
-                        
-                        return identifier;
+                    if (current >= tokens.length || tokens[current].type !== TokenType.RIGHT_BRACKET) {
+                        throw new Error('Expected "]" after table key');
                     }
-                } else {
-                    // Regular parenthesized expression
+                    current++; // Skip ']'
+                    
+                    return {
+                        type: 'TableAccess',
+                        table: { type: 'Identifier', value: identifierValue },
+                        key: keyExpression
+                    };
+                } else if (current < tokens.length && 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
+                    
+                    return {
+                        type: 'TableAccess',
+                        table: { type: 'Identifier', value: identifierValue },
+                        key: { type: 'Identifier', value: propertyName }
+                    };
+                }
+                
+                // Parenthesized expressions are handled as simple grouping, not function calls
+                // This maintains consistency with the juxtaposition pattern
+                if (current < tokens.length && tokens[current].type === TokenType.LEFT_PAREN) {
+                    current++; // consume '('
+                    
+                    // Parse the expression inside parentheses
                     const expression = parseLogicalExpression();
                     
                     if (current >= tokens.length || tokens[current].type !== TokenType.RIGHT_PAREN) {
                         throw new Error('Expected ")" after expression');
                     }
-                    current++;
+                    current++; // consume ')'
                     
                     return expression;
                 }
                 
+                // Juxtaposition function calls are now handled in parseFactor() with proper precedence
+                return { type: 'Identifier', value: identifierValue };
+
+            case TokenType.LEFT_PAREN:
+                current++;
+                if (process.env.DEBUG) {
+                    console.log(`[DEBUG] parsePrimary: parsing LEFT_PAREN, current token = ${tokens[current].type}`);
+                }
+                const expression = parseLogicalExpression();
+                if (current >= tokens.length || tokens[current].type !== TokenType.RIGHT_PAREN) {
+                    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:
+                current++;
+                return { type: 'WildcardPattern' };
+                
             case TokenType.LEFT_BRACE:
                 return parseTableLiteral();
                 
-            case TokenType.NOT:
+
+                
+                                                case TokenType.NOT:
                 current++;
                 const operand = parsePrimary();
-                return { type: 'NotExpression', operand };
+                return { 
+                    type: 'FunctionCall',
+                    name: 'logicalNot',
+                    args: [operand]
+                };
                 
             case TokenType.MINUS:
-                current++;
-                const unaryOperand = parsePrimary();
-                return { type: 'UnaryMinusExpression', operand: unaryOperand };
+                // Delegate unary minus to parseExpression for proper precedence
+                return parseExpression();
                 
             case TokenType.ARROW:
                 current++;
@@ -694,10 +1005,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}`);
         }