Bladeren bron

feat(expressions): add lot literals and global iterate functions

Add support for lot literal syntax [expr1, expr2, ...] to create
collections directly in expressions. Implement GlobalFunctionsElement
to expose global functions like range(), single(), and nothing() for
working with iterables.

- Add LBRACKET/RBRACKET tokens and parsing for lot literals
- Add LotLiteralExpression for evaluating collection literals
- Add GlobalFunctionsElement for namespaced global function access
- Add IterateFunctionAccessor with range, single, nothing functions
- Update FunctionCallExpression to resolve global function calls
- Add comprehensive tests for lot literals and iterate functions
Billy Barrow 1 week geleden
bovenliggende
commit
132e8fd399

+ 1226 - 0
plans/expression-system-design.md

@@ -0,0 +1,1226 @@
+# Expression System Design
+
+## Overview
+
+This document outlines the architecture for a string expression system in the Invercargill library that supports evaluation, translation, and serves as a foundation for future ORM-like functionality.
+
+## Goals
+
+- String expressions for various operations in Invercargill
+- Foundation for ORM-type library (similar to LINQ but with strings instead of expression trees)
+- Support for evaluation AND translation
+- Return value types, objects, and enumerables
+- Boolean expressions: `==`, `!=`, `>=`, `<=`, `<`, `>`, `&&`, `||`
+- Arithmetic expressions: `+`, `-`, `*`, `/`, `%`
+- Unary operators: `-` (negate), `!` (not)
+- Ternary conditional: `condition ? true_value : false_value`
+- Nested expressions with brackets
+- Property navigation via `PropertyAccessor`
+- Function accessors (initially for `Enumerable`)
+- Lambda expressions with closure support
+- Root names provided via `Properties` object
+
+## Example Expressions
+
+```
+a == b
+a.prop
+a.values.where(s => s.rank > a.min_rank)
+a.values.where(s => s.rank > a.min_rank).first().is_important
+```
+
+---
+
+## Architecture
+
+### Core Components Overview
+
+```mermaid
+classDiagram
+    class Expression {
+        <<interface>>
+        +evaluate(EvaluationContext): Element
+        +accept(ExpressionVisitor): void
+        +get_type(): ExpressionType
+    }
+    
+    class EvaluationContext {
+        +Properties root_values
+        +EvaluationContext? parent
+        +Element? get_variable(string name)
+        +EvaluationContext create_child_scope(string param_name, Element value)
+    }
+    
+    class ExpressionVisitor {
+        <<interface>>
+        +visit_literal(LiteralExpression)
+        +visit_variable(VariableExpression)
+        +visit_binary(BinaryExpression)
+        +visit_unary(UnaryExpression)
+        +visit_ternary(TernaryExpression)
+        +visit_property(PropertyExpression)
+        +visit_function_call(FunctionCallExpression)
+        +visit_lambda(LambdaExpression)
+    }
+    
+    Expression <-- LiteralExpression
+    Expression <-- VariableExpression
+    Expression <-- BinaryExpression
+    Expression <-- UnaryExpression
+    Expression <-- TernaryExpression
+    Expression <-- PropertyExpression
+    Expression <-- FunctionCallExpression
+    Expression <-- LambdaExpression
+    Expression <-- BracketedExpression
+    
+    ExpressionVisitor --> Expression: visits
+    EvaluationContext --> Expression: used by
+```
+
+---
+
+## Core Interfaces
+
+### Expression Interface
+
+The base interface for all expression types:
+
+```vala
+namespace Invercargill.Expressions {
+
+    public enum ExpressionType {
+        LITERAL,
+        VARIABLE,
+        BINARY,
+        UNARY,
+        TERNARY,
+        PROPERTY,
+        FUNCTION_CALL,
+        LAMBDA,
+        BRACKETED
+    }
+
+    public interface Expression : Object {
+        public abstract ExpressionType expression_type { get; }
+        
+        // Evaluate the expression with the given context
+        public abstract Element evaluate(EvaluationContext context) throws ExpressionError;
+        
+        // Visitor pattern for translation/traversal
+        public abstract void accept(ExpressionVisitor visitor);
+        
+        // Optional: Get the expected return type (for optimization/validation)
+        public virtual Type? get_return_type() { return null; }
+        
+        // Convenience method for string representation (useful for debugging)
+        public virtual string to_expression_string() { return expression_type.to_string(); }
+    }
+
+}
+```
+
+### EvaluationContext
+
+Manages variable scopes and closures:
+
+```vala
+namespace Invercargill.Expressions {
+
+    public class EvaluationContext : Object {
+        
+        // Root values available in the expression
+        public Properties root_values { get; construct set; }
+        
+        // Parent context for closure support
+        public EvaluationContext? parent { get; construct set; }
+        
+        // Lambda parameter name and value (only in child scopes)
+        private string? _parameter_name;
+        private Element? _parameter_value;
+        
+        public EvaluationContext(Properties root_values) {
+            Object(root_values: root_values);
+        }
+        
+        private EvaluationContext.with_parent(EvaluationContext parent, string param_name, Element param_value) {
+            Object(root_values: parent.root_values, parent: parent);
+            _parameter_name = param_name;
+            _parameter_value = param_value;
+        }
+        
+        // Get a variable by name - searches current scope then parent
+        public Element? get_variable(string name) throws ExpressionError {
+            // Check if it's the lambda parameter
+            if (_parameter_name != null && _parameter_name == name) {
+                return _parameter_value;
+            }
+            
+            // Check parent scope (for closures)
+            if (parent != null) {
+                return parent.get_variable(name);
+            }
+            
+            // Check root values
+            Element element;
+            if (root_values.try_get(name, out element)) {
+                return element;
+            }
+            
+            throw new ExpressionError.NON_EXISTANT_PROPERTY(
+                @"Variable '$name' not found in current scope"
+            );
+        }
+        
+        // Create a child scope for lambda execution
+        public EvaluationContext create_child_scope(string param_name, Element param_value) {
+            return new EvaluationContext.with_parent(this, param_name, param_value);
+        }
+    }
+
+}
+```
+
+### ExpressionVisitor Interface
+
+For translation and traversal:
+
+```vala
+namespace Invercargill.Expressions {
+
+    public interface ExpressionVisitor : Object {
+        public virtual void visit_literal(LiteralExpression expr) {}
+        public virtual void visit_variable(VariableExpression expr) {}
+        public virtual void visit_binary(BinaryExpression expr) {}
+        public virtual void visit_unary(UnaryExpression expr) {}
+        public virtual void visit_ternary(TernaryExpression expr) {}
+        public virtual void visit_property(PropertyExpression expr) {}
+        public virtual void visit_function_call(FunctionCallExpression expr) {}
+        public virtual void visit_lambda(LambdaExpression expr) {}
+        public virtual void visit_bracketed(BracketedExpression expr) {}
+    }
+
+}
+```
+
+---
+
+## Expression Types
+
+### 1. LiteralExpression
+
+Wraps constant values:
+
+```vala
+public class LiteralExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.LITERAL; } }
+    
+    public Element value { get; construct set; }
+    
+    public LiteralExpression(Element value) {
+        Object(value: value);
+    }
+    
+    // Convenience constructors
+    public LiteralExpression.for_string(string val) {
+        this(new ValueElement(val));
+    }
+    
+    public LiteralExpression.for_int(int val) {
+        this(new ValueElement(val));
+    }
+    
+    public LiteralExpression.for_bool(bool val) {
+        this(new ValueElement(val));
+    }
+    
+    public LiteralExpression.for_null() {
+        this(new NullElement());
+    }
+    
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        return value;
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_literal(this);
+    }
+}
+```
+
+### 2. VariableExpression
+
+References a named variable from the context:
+
+```vala
+public class VariableExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.VARIABLE; } }
+    
+    public string variable_name { get; construct set; }
+    
+    public VariableExpression(string name) {
+        Object(variable_name: name);
+    }
+    
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        return context.get_variable(variable_name);
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_variable(this);
+    }
+}
+```
+
+### 3. BinaryExpression
+
+Handles binary operators:
+
+```vala
+public enum BinaryOperator {
+    // Comparison
+    EQUAL,           // ==
+    NOT_EQUAL,       // !=
+    GREATER_THAN,    // >
+    LESS_THAN,       // <
+    GREATER_EQUAL,   // >=
+    LESS_EQUAL,      // <=
+    
+    // Logical
+    AND,             // &&
+    OR,              // ||
+    
+    // Arithmetic
+    ADD,             // +
+    SUBTRACT,        // -
+    MULTIPLY,        // *
+    DIVIDE,          // /
+    MODULO;          // %
+    
+    public string to_string() {
+        switch (this) {
+            case EQUAL: return "==";
+            case NOT_EQUAL: return "!=";
+            case GREATER_THAN: return ">";
+            case LESS_THAN: return "<";
+            case GREATER_EQUAL: return ">=";
+            case LESS_EQUAL: return "<=";
+            case AND: return "&&";
+            case OR: return "||";
+            case ADD: return "+";
+            case SUBTRACT: return "-";
+            case MULTIPLY: return "*";
+            case DIVIDE: return "/";
+            case MODULO: return "%";
+            default: return "";
+        }
+    }
+    
+    public bool is_comparison() {
+        return this in { EQUAL, NOT_EQUAL, GREATER_THAN, LESS_THAN, GREATER_EQUAL, LESS_EQUAL };
+    }
+    
+    public bool is_logical() {
+        return this in { AND, OR };
+    }
+    
+    public bool is_arithmetic() {
+        return this in { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULO };
+    }
+}
+
+public class BinaryExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.BINARY; } }
+    
+    public Expression left { get; construct set; }
+    public Expression right { get; construct set; }
+    public BinaryOperator operator { get; construct set; }
+    
+    public BinaryExpression(Expression left, BinaryOperator op, Expression right) {
+        Object(left: left, right: right, operator: op);
+    }
+    
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        var left_val = left.evaluate(context);
+        var right_val = right.evaluate(context);
+        
+        switch (operator) {
+            case BinaryOperator.AND:
+                return new ValueElement(evaluate_bool(left_val) && evaluate_bool(right_val));
+            case BinaryOperator.OR:
+                return new ValueElement(evaluate_bool(left_val) || evaluate_bool(right_val));
+            case BinaryOperator.EQUAL:
+                return new ValueElement(evaluate_equality(left_val, right_val));
+            case BinaryOperator.NOT_EQUAL:
+                return new ValueElement(!evaluate_equality(left_val, right_val));
+            case BinaryOperator.GREATER_THAN:
+            case BinaryOperator.LESS_THAN:
+            case BinaryOperator.GREATER_EQUAL:
+            case BinaryOperator.LESS_EQUAL:
+                return new ValueElement(evaluate_comparison(left_val, right_val, operator));
+            case BinaryOperator.ADD:
+                return evaluate_arithmetic(left_val, right_val, (a, b) => a + b, (a, b) => a + b);
+            case BinaryOperator.SUBTRACT:
+                return evaluate_arithmetic(left_val, right_val, (a, b) => a - b, (a, b) => a - b);
+            case BinaryOperator.MULTIPLY:
+                return evaluate_arithmetic(left_val, right_val, (a, b) => a * b, (a, b) => a * b);
+            case BinaryOperator.DIVIDE:
+                return evaluate_arithmetic(left_val, right_val, (a, b) => a / b, (a, b) => a / b);
+            case BinaryOperator.MODULO:
+                return evaluate_arithmetic_int(left_val, right_val, (a, b) => a % b);
+        }
+    }
+    
+    private Element evaluate_arithmetic(
+        Element left,
+        Element right,
+        owned ComputeIntDelegate int_op,
+        owned ComputeDoubleDelegate double_op
+    ) throws ExpressionError {
+        // Try double first for mixed arithmetic
+        double? left_d, right_d;
+        if (left.try_get_as(out left_d) && right.try_get_as(out right_d)) {
+            return new ValueElement(double_op(left_d, right_d));
+        }
+        
+        // Try int
+        int? left_i, right_i;
+        if (left.try_get_as(out left_i) && right.try_get_as(out right_i)) {
+            return new ValueElement(int_op(left_i, right_i));
+        }
+        
+        // String concatenation for + operator
+        if (operator == BinaryOperator.ADD) {
+            string? left_s, right_s;
+            if (left.try_get_as(out left_s) && right.try_get_as(out right_s)) {
+                return new ValueElement(left_s + right_s);
+            }
+        }
+        
+        throw new ExpressionError.INVALID_TYPE(
+            @"Cannot perform arithmetic on $(left.type_name()) and $(right.type_name())"
+        );
+    }
+    
+    private Element evaluate_arithmetic_int(
+        Element left,
+        Element right,
+        owned ComputeIntDelegate op
+    ) throws ExpressionError {
+        int? left_i, right_i;
+        if (left.try_get_as(out left_i) && right.try_get_as(out right_i)) {
+            return new ValueElement(op(left_i, right_i));
+        }
+        throw new ExpressionError.INVALID_TYPE("Modulo operator requires integer operands");
+    }
+    
+    private delegate int ComputeIntDelegate(int a, int b);
+    private delegate double ComputeDoubleDelegate(double a, double b);
+    
+    private bool evaluate_bool(Element element) throws ExpressionError {
+        if (element.is<bool>()) {
+            return element.as<bool>();
+        }
+        throw new ExpressionError.INVALID_TYPE("Expected boolean value");
+    }
+    
+    private bool evaluate_equality(Element left, Element right) {
+        // Use existing equality operators from Invercargill.Operators
+        // Handle null cases
+        if (left.is_null() && right.is_null()) return true;
+        if (left.is_null() || right.is_null()) return false;
+        
+        // Try to compare using Element's try_get_as
+        // ... implementation details
+    }
+    
+    private bool evaluate_comparison(Element left, Element right, BinaryOperator op) throws ExpressionError {
+        // Use existing comparison operators from Invercargill.Operators
+        // ... implementation details
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_binary(this);
+        left.accept(visitor);
+        right.accept(visitor);
+    }
+    
+    public override string to_expression_string() {
+        return @"($(_left.to_expression_string()) $(operator.to_string()) $(_right.to_expression_string()))";
+    }
+}
+```
+
+### 4. UnaryExpression
+
+Handles unary operators (negation, etc.):
+
+```vala
+public enum UnaryOperator {
+    NEGATE,    // - (arithmetic negation)
+    NOT,       // ! (logical negation)
+    INCREMENT, // ++ (prefix/postfix increment)
+    DECREMENT; // -- (prefix/postfix decrement)
+    
+    public string to_string() {
+        switch (this) {
+            case NEGATE: return "-";
+            case NOT: return "!";
+            case INCREMENT: return "++";
+            case DECREMENT: return "--";
+            default: return "";
+        }
+    }
+}
+
+public class UnaryExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.UNARY; } }
+    
+    public Expression operand { get; construct set; }
+    public UnaryOperator operator { get; construct set; }
+    public bool is_prefix { get; construct set; } // true for ++x, false for x++
+    
+    public UnaryExpression(UnaryOperator op, Expression operand, bool is_prefix = true) {
+        Object(operand: operand, operator: op, is_prefix: is_prefix);
+    }
+    
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        var operand_val = operand.evaluate(context);
+        
+        switch (operator) {
+            case UnaryOperator.NEGATE:
+                return evaluate_negate(operand_val);
+            case UnaryOperator.NOT:
+                return evaluate_not(operand_val);
+            case UnaryOperator.INCREMENT:
+            case UnaryOperator.DECREMENT:
+                throw new ExpressionError.INVALID_SYNTAX(
+                    "Increment/decrement operators not supported in expression evaluation"
+                );
+        }
+    }
+    
+    private Element evaluate_negate(Element element) throws ExpressionError {
+        // Try numeric types
+        int? int_val;
+        if (element.try_get_as(out int_val)) {
+            return new ValueElement(-int_val);
+        }
+        
+        double? double_val;
+        if (element.try_get_as(out double_val)) {
+            return new ValueElement(-double_val);
+        }
+        
+        int64? int64_val;
+        if (element.try_get_as(out int64_val)) {
+            return new ValueElement(-int64_val);
+        }
+        
+        throw new ExpressionError.INVALID_TYPE("Cannot negate non-numeric value");
+    }
+    
+    private Element evaluate_not(Element element) throws ExpressionError {
+        if (element.is<bool>()) {
+            return new ValueElement(!element.as<bool>());
+        }
+        throw new ExpressionError.INVALID_TYPE("Logical NOT requires boolean operand");
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_unary(this);
+        operand.accept(visitor);
+    }
+    
+    public override string to_expression_string() {
+        if (is_prefix) {
+            return @"$(operator.to_string())$(operand.to_expression_string())";
+        }
+        return @"$(operand.to_expression_string())$(operator.to_string())";
+    }
+}
+```
+
+### 5. TernaryExpression
+
+Handles conditional expressions (ternary operator):
+
+```vala
+public class TernaryExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.TERNARY; } }
+    
+    public Expression condition { get; construct set; }
+    public Expression true_expression { get; construct set; }
+    public Expression false_expression { get; construct set; }
+    
+    public TernaryExpression(Expression condition, Expression true_expr, Expression false_expr) {
+        Object(condition: condition, true_expression: true_expr, false_expression: false_expr);
+    }
+    
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        var cond_val = condition.evaluate(context);
+        
+        bool cond_bool;
+        if (cond_val.try_get_as(out cond_bool)) {
+            if (cond_bool) {
+                return true_expression.evaluate(context);
+            } else {
+                return false_expression.evaluate(context);
+            }
+        }
+        
+        throw new ExpressionError.INVALID_TYPE("Ternary condition must evaluate to boolean");
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_ternary(this);
+        condition.accept(visitor);
+        true_expression.accept(visitor);
+        false_expression.accept(visitor);
+    }
+    
+    public override string to_expression_string() {
+        return @"$(condition.to_expression_string()) ? $(true_expression.to_expression_string()) : $(false_expression.to_expression_string())";
+    }
+}
+```
+
+### 6. PropertyExpression
+
+Navigates properties using `PropertyAccessor`:
+
+```vala
+public class PropertyExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.PROPERTY; } }
+    
+    public Expression target { get; construct set; }
+    public string property_name { get; construct set; }
+    
+    public PropertyExpression(Expression target, string property_name) {
+        Object(target: target, property_name: property_name);
+    }
+    
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        var target_value = target.evaluate(context);
+        
+        // Get PropertyAccessor from the element
+        PropertyAccessor? accessor = null;
+        
+        // If the element itself is a PropertyAccessor
+        if (target_value.try_get_as(out accessor)) {
+            return accessor.read_property(property_name);
+        }
+        
+        // If it's an Object, wrap it
+        Object? obj;
+        if (target_value.try_get_as(out obj)) {
+            accessor = new ObjectPropertyAccessor(obj);
+            return accessor.read_property(property_name);
+        }
+        
+        // If it's a Properties instance
+        Properties? props;
+        if (target_value.try_get_as(out props)) {
+            accessor = new PropertiesPropertyAccessor(props);
+            return accessor.read_property(property_name);
+        }
+        
+        throw new ExpressionError.INVALID_TYPE(
+            @"Cannot access property '$property_name' on type $(target_value.type_name())"
+        );
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_property(this);
+        target.accept(visitor);
+    }
+}
+```
+
+### 7. FunctionCallExpression
+
+Calls functions via `FunctionAccessor`:
+
+```vala
+public class FunctionCallExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.FUNCTION_CALL; } }
+    
+    public Expression target { get; construct set; }
+    public string function_name { get; construct set; }
+    public Gee.List<Expression> arguments { get; construct set; }
+    
+    public FunctionCallExpression(Expression target, string function_name, Gee.List<Expression>? arguments = null) {
+        Object(target: target, function_name: function_name, arguments: arguments ?? new Gee.ArrayList<Expression>());
+    }
+    
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        var target_value = target.evaluate(context);
+        
+        // Get FunctionAccessor from the element
+        FunctionAccessor? accessor = get_function_accessor(target_value);
+        
+        if (accessor == null) {
+            throw new ExpressionError.INVALID_TYPE(
+                @"Cannot call function '$function_name' on type $(target_value.type_name())"
+            );
+        }
+        
+        // Evaluate arguments
+        var evaluated_args = new Gee.ArrayList<Element>();
+        foreach (var arg in arguments) {
+            evaluated_args.add(arg.evaluate(context));
+        }
+        
+        return accessor.call_function(function_name, evaluated_args, context);
+    }
+    
+    private FunctionAccessor? get_function_accessor(Element element) {
+        // Check if element directly implements FunctionAccessor
+        FunctionAccessor? accessor;
+        if (element.try_get_as(out accessor)) {
+            return accessor;
+        }
+        
+        // Check for Elements (Enumerable wrapper)
+        Elements? elements;
+        if (element.try_get_as(out elements)) {
+            return new EnumerableFunctionAccessor(elements);
+        }
+        
+        return null;
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_function_call(this);
+        target.accept(visitor);
+        foreach (var arg in arguments) {
+            arg.accept(visitor);
+        }
+    }
+}
+```
+
+### 8. LambdaExpression
+
+Represents lambda functions with closure support:
+
+```vala
+public class LambdaExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.LAMBDA; } }
+    
+    public string parameter_name { get; construct set; }
+    public Expression body { get; construct set; }
+    
+    // Capture the context at creation time for closures
+    private EvaluationContext? _captured_context;
+    
+    public LambdaExpression(string parameter_name, Expression body) {
+        Object(parameter_name: parameter_name, body: body);
+    }
+    
+    // Called when the lambda is created in an expression
+    public void capture_context(EvaluationContext context) {
+        _captured_context = context;
+    }
+    
+    // Evaluate the lambda with a specific argument
+    public Element evaluate_with_argument(Element argument) throws ExpressionError {
+        if (_captured_context == null) {
+            throw new ExpressionError.INVALID_TYPE("Lambda has no captured context");
+        }
+        
+        // Create child scope with the parameter
+        var lambda_context = _captured_context.create_child_scope(parameter_name, argument);
+        
+        return body.evaluate(lambda_context);
+    }
+    
+    // Standard evaluate returns the lambda itself as a callable
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        capture_context(context);
+        return new LambdaElement(this);
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_lambda(this);
+        body.accept(visitor);
+    }
+}
+```
+
+### 9. BracketedExpression
+
+Handles parenthesized expressions for precedence:
+
+```vala
+public class BracketedExpression : Object, Expression {
+    public ExpressionType expression_type { get { return ExpressionType.BRACKETED; } }
+    
+    public Expression inner { get; construct set; }
+    
+    public BracketedExpression(Expression inner) {
+        Object(inner: inner);
+    }
+    
+    public Element evaluate(EvaluationContext context) throws ExpressionError {
+        return inner.evaluate(context);
+    }
+    
+    public void accept(ExpressionVisitor visitor) {
+        visitor.visit_bracketed(this);
+        inner.accept(visitor);
+    }
+}
+```
+
+---
+
+## Supporting Types
+
+### LambdaElement
+
+Wraps a lambda for passing around:
+
+```vala
+public class LambdaElement : Object, Element {
+    private LambdaExpression _lambda;
+    
+    public LambdaElement(LambdaExpression lambda) {
+        _lambda = lambda;
+    }
+    
+    public LambdaExpression get_lambda() {
+        return _lambda;
+    }
+    
+    // Element interface implementation
+    public bool assignable_to_type(Type type) {
+        return type == typeof(LambdaElement) || type == typeof(LambdaExpression);
+    }
+    
+    public Type? type() {
+        return typeof(LambdaExpression);
+    }
+    
+    public bool is_null() {
+        return false;
+    }
+    
+    public bool try_get_as<T>(out T result) {
+        if (typeof(T) == typeof(LambdaElement)) {
+            result = this;
+            return true;
+        }
+        if (typeof(T) == typeof(LambdaExpression)) {
+            result = _lambda;
+            return true;
+        }
+        result = null;
+        return false;
+    }
+}
+```
+
+---
+
+## FunctionAccessor Interface
+
+```vala
+namespace Invercargill.Expressions {
+
+    public interface FunctionAccessor : Object {
+        
+        // Check if a function is available
+        public abstract bool has_function(string function_name);
+        
+        // Get available function names
+        public abstract Enumerable<string> get_function_names();
+        
+        // Call a function with evaluated arguments
+        // The context is provided for lambdas that need to capture scope
+        public abstract Element call_function(
+            string function_name,
+            Gee.List<Element> arguments,
+            EvaluationContext context
+        ) throws ExpressionError;
+    }
+
+}
+```
+
+### EnumerableFunctionAccessor
+
+Implementation for Enumerable/Elements:
+
+```vala
+public class EnumerableFunctionAccessor : Object, FunctionAccessor {
+    
+    private Elements _target;
+    
+    public EnumerableFunctionAccessor(Elements target) {
+        _target = target;
+    }
+    
+    public bool has_function(string function_name) {
+        return get_supported_functions().contains(function_name);
+    }
+    
+    public Enumerable<string> get_function_names() {
+        return get_supported_functions();
+    }
+    
+    private Enumerable<string> get_supported_functions() {
+        return Iterate.from_array(new string[] {
+            "where", "select", "first", "last", "single",
+            "take", "skip", "count", "any", "all",
+            "order_by", "order_by_descending", "distinct",
+            "element_at", "concat", "reverse"
+        });
+    }
+    
+    public Element call_function(
+        string function_name,
+        Gee.List<Element> arguments,
+        EvaluationContext context
+    ) throws ExpressionError {
+        
+        switch (function_name.to_lower()) {
+            case "where":
+                return call_where(arguments, context);
+            case "first":
+                return call_first(arguments);
+            case "last":
+                return call_last(arguments);
+            case "count":
+                return call_count(arguments);
+            case "any":
+                return call_any(arguments, context);
+            // ... other methods
+            
+            default:
+                throw new ExpressionError.INVALID_TYPE(
+                    @"Unknown function '$function_name' for Enumerable"
+                );
+        }
+    }
+    
+    private Element call_where(Gee.List<Element> arguments, EvaluationContext context) throws ExpressionError {
+        if (arguments.size != 1) {
+            throw new ExpressionError.INVALID_SYNTAX("where() requires exactly 1 argument");
+        }
+        
+        var lambda_element = arguments[0];
+        LambdaExpression? lambda;
+        
+        if (!lambda_element.try_get_as(out lambda)) {
+            throw new ExpressionError.INVALID_TYPE("where() requires a lambda argument");
+        }
+        
+        // Capture the current context for closure support
+        lambda.capture_context(context);
+        
+        // Apply the where filter
+        var filtered = _target.where(item => {
+            try {
+                var result = lambda.evaluate_with_argument(new NativeElement<Object>(item));
+                return result.as<bool>();
+            } catch (Error e) {
+                return false;
+            }
+        });
+        
+        return new NativeElement<Elements>(filtered.to_elements());
+    }
+    
+    private Element call_first(Gee.List<Element> arguments) throws ExpressionError {
+        if (arguments.size > 1) {
+            throw new ExpressionError.INVALID_SYNTAX("first() takes at most 1 argument");
+        }
+        
+        try {
+            if (arguments.size == 0) {
+                return new NativeElement<Object?>(_target.first());
+            }
+            
+            // With predicate lambda
+            var lambda_element = arguments[0];
+            LambdaExpression? lambda;
+            if (!lambda_element.try_get_as(out lambda)) {
+                throw new ExpressionError.INVALID_TYPE("first() predicate must be a lambda");
+            }
+            
+            var result = _target.first(item => {
+                try {
+                    var eval_result = lambda.evaluate_with_argument(new NativeElement<Object>(item));
+                    return eval_result.as<bool>();
+                } catch (Error e) {
+                    return false;
+                }
+            });
+            
+            return new NativeElement<Object?>(result);
+        } catch (SequenceError e) {
+            return new NullElement();
+        }
+    }
+    
+    // ... other method implementations
+}
+```
+
+---
+
+## Expression Evaluator
+
+A facade for evaluating expressions:
+
+```vala
+public class ExpressionEvaluator : Object {
+    
+    public Element evaluate(Expression expression, Properties root_values) throws ExpressionError {
+        var context = new EvaluationContext(root_values);
+        return expression.evaluate(context);
+    }
+    
+    public T evaluate_as<T>(Expression expression, Properties root_values) throws Error {
+        var result = evaluate(expression, root_values);
+        return result.as<T>();
+    }
+    
+    // Convenience method for creating and evaluating a simple property chain
+    public Element evaluate_property_chain(string[] properties, Properties root_values) throws ExpressionError {
+        if (properties.length == 0) {
+            throw new ExpressionError.INVALID_SYNTAX("Property chain cannot be empty");
+        }
+        
+        Expression current = new VariableExpression(properties[0]);
+        
+        for (int i = 1; i < properties.length; i++) {
+            current = new PropertyExpression(current, properties[i]);
+        }
+        
+        return evaluate(current, root_values);
+    }
+}
+```
+
+---
+
+## Translation Pattern
+
+For ORM-like translation, implement `ExpressionVisitor`:
+
+```vala
+public class SqlExpressionTranslator : Object, ExpressionVisitor {
+    
+    private StringBuilder _builder = new StringBuilder();
+    private int _parameter_index = 0;
+    private Gee.ArrayList<Value> _parameters = new Gee.ArrayList<Value>();
+    
+    public string translate(Expression expression) {
+        _builder.truncate(0);
+        _parameters.clear();
+        _parameter_index = 0;
+        
+        expression.accept(this);
+        
+        return _builder.str;
+    }
+    
+    public Gee.List<Value> get_parameters() {
+        return _parameters.read_only_view;
+    }
+    
+    public override void visit_literal(LiteralExpression expr) {
+        // Add parameter placeholder
+        _parameters.add(extract_value(expr.value));
+        _builder.append(@"@p$(_parameter_index)");
+        _parameter_index++;
+    }
+    
+    public override void visit_variable(VariableExpression expr) {
+        // Translate variable to column reference
+        _builder.append(expr.variable_name);
+    }
+    
+    public override void visit_binary(BinaryExpression expr) {
+        _builder.append("(");
+        expr.left.accept(this);
+        _builder.append(" ");
+        _builder.append(expr.operator.to_string());
+        _builder.append(" ");
+        expr.right.accept(this);
+        _builder.append(")");
+    }
+    
+    public override void visit_unary(UnaryExpression expr) {
+        if (expr.is_prefix) {
+            _builder.append(expr.operator.to_string());
+            expr.operand.accept(this);
+        } else {
+            expr.operand.accept(this);
+            _builder.append(expr.operator.to_string());
+        }
+    }
+    
+    public override void visit_ternary(TernaryExpression expr) {
+        expr.condition.accept(this);
+        _builder.append(" CASE WHEN ");
+        // For SQL, we translate ternary to CASE WHEN ... THEN ... ELSE ... END
+        expr.true_expression.accept(this);
+        _builder.append(" ELSE ");
+        expr.false_expression.accept(this);
+        _builder.append(" END");
+    }
+    
+    public override void visit_property(PropertyExpression expr) {
+        expr.target.accept(this);
+        _builder.append(".");
+        _builder.append(expr.property_name);
+    }
+    
+    public override void visit_function_call(FunctionCallExpression expr) {
+        // Handle specific functions differently
+        // e.g., where() becomes part of WHERE clause
+        // ... implementation
+    }
+    
+    public override void visit_lambda(LambdaExpression expr) {
+        // Lambda body becomes part of the SQL expression
+        expr.body.accept(this);
+    }
+}
+```
+
+---
+
+## File Structure
+
+```
+src/lib/Expressions/
+├── Expression.vala              # Main Expression interface
+├── ExpressionError.vala         # Error domain (existing)
+├── ExpressionType.vala          # ExpressionType enum
+├── EvaluationContext.vala       # Context for evaluation
+├── ExpressionVisitor.vala       # Visitor interface
+├── ExpressionEvaluator.vala     # Facade for evaluation
+│
+├── PropertyAccessor.vala        # Interface (existing)
+├── ObjectPropertyAccessor.vala  # Implementation (existing)
+├── PropertiesPropertyAccessor.vala # Implementation (existing)
+│
+├── FunctionAccessor.vala        # Interface for function calls
+├── EnumerableFunctionAccessor.vala # Implementation for Enumerable
+│
+├── Expressions/
+│   ├── LiteralExpression.vala
+│   ├── VariableExpression.vala
+│   ├── BinaryExpression.vala
+│   ├── BinaryOperator.vala
+│   ├── UnaryExpression.vala
+│   ├── UnaryOperator.vala
+│   ├── TernaryExpression.vala
+│   ├── PropertyExpression.vala
+│   ├── FunctionCallExpression.vala
+│   ├── LambdaExpression.vala
+│   └── BracketedExpression.vala
+│
+├── Elements/
+│   └── LambdaElement.vala       # Element wrapper for lambdas
+│
+└── Translators/
+    ├── SqlExpressionTranslator.vala
+    └── StructuredExpressionTranslator.vala
+```
+
+---
+
+## Usage Examples
+
+### Basic Variable and Property Access
+
+```vala
+var props = new PropertiesDictionary();
+props.set_native("a", some_object);
+props.set_native("b", 42);
+
+var expr = new BinaryExpression(
+    new PropertyExpression(new VariableExpression("a"), "value"),
+    BinaryOperator.GREATER_THAN,
+    new VariableExpression("b")
+);
+
+var evaluator = new ExpressionEvaluator();
+var result = evaluator.evaluate_as<bool>(expr, props);
+```
+
+### Lambda with Closure
+
+```vala
+// Expression: a.values.where(s => s.rank > a.min_rank)
+var props = new PropertiesDictionary();
+props.set_native("a", container_with_values_and_min_rank);
+
+// Build: a.values
+var a_values = new PropertyExpression(
+    new VariableExpression("a"),
+    "values"
+);
+
+// Build: s.rank > a.min_rank
+var lambda_body = new BinaryExpression(
+    new PropertyExpression(new VariableExpression("s"), "rank"),
+    BinaryOperator.GREATER_THAN,
+    new PropertyExpression(new VariableExpression("a"), "min_rank")
+);
+
+// Build: s => s.rank > a.min_rank
+var lambda = new LambdaExpression("s", lambda_body);
+
+// Build: a.values.where(s => s.rank > a.min_rank)
+var where_call = new FunctionCallExpression(a_values, "where", 
+    new Gee.ArrayList<Expression>.wrap({ lambda })
+);
+
+var evaluator = new ExpressionEvaluator();
+var result = evaluator.evaluate(where_call, props);
+```
+
+### Chained Function Calls
+
+```vala
+// Expression: a.values.where(s => s.rank > a.min_rank).first().is_important
+// ... previous where_call ...
+
+var first_call = new FunctionCallExpression(where_call, "first");
+var is_important = new PropertyExpression(first_call, "is_important");
+
+var evaluator = new ExpressionEvaluator();
+var result = evaluator.evaluate_as<bool>(is_important, props);
+```
+
+---
+
+## Design Decisions Summary
+
+1. **Dynamically Typed**: Uses `Element` system for flexible type handling
+2. **Closure Support**: `EvaluationContext` maintains parent chain for scope resolution
+3. **Visitor Pattern**: Enables translation without modifying expression classes
+4. **Interface-Based Accessors**: Both `PropertyAccessor` and `FunctionAccessor` are interfaces for extensibility
+5. **Separation of Concerns**: 
+   - Expressions define structure
+   - Evaluators handle execution
+   - Visitors handle translation
+6. **ORM Ready**: The visitor pattern allows for SQL or other query language generation
+
+---
+
+## Next Steps
+
+1. Implement core interfaces (`Expression`, `EvaluationContext`, `ExpressionVisitor`)
+2. Implement expression types (start with `LiteralExpression`, `VariableExpression`, `BinaryExpression`)
+3. Implement `FunctionAccessor` interface and `EnumerableFunctionAccessor`
+4. Add `LambdaExpression` with closure capture
+5. Add `ExpressionEvaluator` facade
+6. Write unit tests
+7. Add parser (future task)
+8. Add SQL translator (future task)

+ 89 - 0
src/lib/Expressions/Elements/GlobalFunctionsElement.vala

@@ -0,0 +1,89 @@
+using Invercargill.DataStructures;
+
+namespace Invercargill.Expressions {
+
+    /**
+     * Element that provides access to global function namespaces.
+     * 
+     * This element wraps a FunctionAccessor and provides it as a callable
+     * element. It's used for global function namespaces like Iterate.
+     * 
+     * Example usage:
+     * ```vala
+     * var iterate_element = new GlobalFunctionsElement(new IterateFunctionAccessor());
+     * context.set_variable("Iterate", iterate_element);
+     * // Now you can call: Iterate.range(1, 10)
+     * ```
+     */
+    public class GlobalFunctionsElement : Object, Element {
+
+        private FunctionAccessor _accessor;
+
+        /**
+         * Creates a new GlobalFunctionsElement with the given accessor.
+         * 
+         * @param accessor The function accessor to wrap
+         */
+        public GlobalFunctionsElement(FunctionAccessor accessor) {
+            _accessor = accessor;
+        }
+
+        /**
+         * The wrapped function accessor.
+         */
+        public FunctionAccessor accessor {
+            get { return _accessor; }
+        }
+
+        /**
+         * Checks if this element can be assigned to the given type.
+         */
+        public bool assignable_to_type(GLib.Type type) {
+            return type == typeof(FunctionAccessor) || type == typeof(GlobalFunctionsElement);
+        }
+
+        /**
+         * Returns the type of the wrapped value.
+         */
+        public GLib.Type? type() {
+            return typeof(FunctionAccessor);
+        }
+
+        /**
+         * Returns a string representation.
+         */
+        public string type_name() {
+            return "GlobalFunctions";
+        }
+
+        /**
+         * Attempts to convert this element to the specified type.
+         */
+        public bool try_get_as<T>(out T result) {
+            if (typeof(T) == typeof(FunctionAccessor) || typeof(T).is_a(typeof(FunctionAccessor))) {
+                result = (T)_accessor;
+                return true;
+            }
+            if (typeof(T) == typeof(GlobalFunctionsElement)) {
+                result = (T)this;
+                return true;
+            }
+            result = null;
+            return false;
+        }
+
+        /**
+         * Returns false as this is not a null element.
+         */
+        public bool is_null() {
+            return false;
+        }
+
+        /**
+         * Returns a string representation of this element.
+         */
+        public string to_string() {
+            return "GlobalFunctions";
+        }
+    }
+}

+ 29 - 1
src/lib/Expressions/ExpressionParser.vala

@@ -241,8 +241,13 @@ namespace Invercargill.Expressions {
             return expr;
         }
 
-        // Primary: literals, variables, parentheses, lambdas
+        // Primary: literals, variables, parentheses, lambdas, lot literals
         private Expression parse_primary() throws ExpressionError {
+            // Lot literal: [expr1, expr2, ...]
+            if (match(TokenType.LBRACKET)) {
+                return parse_lot_literal();
+            }
+
             // Parenthesized expression or lambda
             if (match(TokenType.LPAREN)) {
                 // Check if this is a lambda: (x) => expr or () => expr
@@ -381,6 +386,29 @@ namespace Invercargill.Expressions {
             return args;
         }
 
+        // Parse lot literal: [expr1, expr2, ...]
+        private Expression parse_lot_literal() throws ExpressionError {
+            var elements = new Series<Expression>();
+
+            // Empty lot: []
+            if (check(TokenType.RBRACKET)) {
+                advance();
+                return new LotLiteralExpression(new Expression[0]);
+            }
+
+            // Parse first element
+            elements.add(parse_ternary());
+
+            // Parse remaining elements
+            while (match(TokenType.COMMA)) {
+                elements.add(parse_ternary());
+            }
+
+            expect(TokenType.RBRACKET, "Expected ']' after lot literal elements");
+
+            return new LotLiteralExpression(elements.to_array());
+        }
+
         // ==================== Helper Methods ====================
 
         private bool is_at_end() {

+ 10 - 0
src/lib/Expressions/ExpressionTokenizer.vala

@@ -39,6 +39,8 @@ namespace Invercargill.Expressions {
         COMMA,          // ,
         LPAREN,         // (
         RPAREN,         // )
+        LBRACKET,       // [
+        RBRACKET,       // ]
         QUESTION,       // ?
         COLON,          // :
         ARROW,          // =>
@@ -74,6 +76,8 @@ namespace Invercargill.Expressions {
                 case COMMA: return ",";
                 case LPAREN: return "(";
                 case RPAREN: return ")";
+                case LBRACKET: return "[";
+                case RBRACKET: return "]";
                 case QUESTION: return "?";
                 case COLON: return ":";
                 case ARROW: return "=>";
@@ -184,6 +188,12 @@ namespace Invercargill.Expressions {
                 case ')':
                     _position++;
                     return new Token(TokenType.RPAREN, ")", start_pos);
+                case '[':
+                    _position++;
+                    return new Token(TokenType.LBRACKET, "[", start_pos);
+                case ']':
+                    _position++;
+                    return new Token(TokenType.RBRACKET, "]", start_pos);
                 case '?':
                     _position++;
                     return new Token(TokenType.QUESTION, "?", start_pos);

+ 3 - 1
src/lib/Expressions/ExpressionType.vala

@@ -13,7 +13,8 @@ namespace Invercargill.Expressions {
         PROPERTY,
         FUNCTION_CALL,
         LAMBDA,
-        BRACKETED;
+        BRACKETED,
+        LOT_LITERAL;
 
         public string to_string() {
             switch (this) {
@@ -26,6 +27,7 @@ namespace Invercargill.Expressions {
                 case FUNCTION_CALL: return "FunctionCall";
                 case LAMBDA: return "Lambda";
                 case BRACKETED: return "Bracketed";
+                case LOT_LITERAL: return "LotLiteral";
                 default: return "Unknown";
             }
         }

+ 5 - 0
src/lib/Expressions/ExpressionVisitor.vala

@@ -60,6 +60,11 @@ namespace Invercargill.Expressions {
          */
         public virtual void visit_bracketed(BracketedExpression expr) {}
 
+        /**
+         * Visit a lot literal expression.
+         */
+        public virtual void visit_lot_literal(LotLiteralExpression expr) {}
+
     }
 
 }

+ 5 - 0
src/lib/Expressions/Expressions/FunctionCallExpression.vala

@@ -117,6 +117,11 @@ namespace Invercargill.Expressions {
         // Private helper methods
 
         private FunctionAccessor? get_function_accessor(Element element) {
+            // Check for GlobalFunctionsElement first
+            if (element is GlobalFunctionsElement) {
+                return ((GlobalFunctionsElement)element).accessor;
+            }
+
             // Check if element directly implements FunctionAccessor
             FunctionAccessor? accessor;
             if (element.try_get_as(out accessor)) {

+ 292 - 0
src/lib/Expressions/Expressions/LotLiteralExpression.vala

@@ -0,0 +1,292 @@
+using Invercargill.DataStructures;
+
+namespace Invercargill.Expressions {
+
+    /**
+     * Expression representing a literal lot/array of values.
+     * 
+     * Lot literals use the syntax `[expr1, expr2, expr3]` and create
+     * an Enumerable containing all the evaluated elements.
+     * 
+     * All elements must be of compatible types. The expression will
+     * attempt to find a common type for all elements.
+     * 
+     * Examples:
+     * - `[1, 2, 3]` - creates a lot of integers
+     * - `["a", "b", "c"]` - creates a lot of strings
+     * - `[a, b, c]` - creates a lot from variable values
+     * - `[1 + 2, 3 * 4, 5 - 6]` - creates a lot from expressions
+     */
+    public class LotLiteralExpression : Object, Expression {
+
+        private Expression[] _elements;
+
+        /**
+         * Creates a new lot literal expression.
+         * 
+         * @param elements the expressions that will be evaluated to produce the lot elements
+         */
+        public LotLiteralExpression(Expression[] elements) {
+            _elements = elements;
+        }
+
+        /**
+         * The expressions that produce the lot elements.
+         */
+        public Expression[] elements {
+            get { return _elements; }
+        }
+
+        public ExpressionType expression_type {
+            get { return ExpressionType.LOT_LITERAL; }
+        }
+
+        public Element evaluate(EvaluationContext context) throws ExpressionError {
+            // If empty lot, return an empty enumerable of objects
+            if (_elements.length == 0) {
+                return new NativeElement<Enumerable<Object?>>(Iterate.nothing<Object?>());
+            }
+
+            // Evaluate all elements first
+            Element[] evaluated = new Element[_elements.length];
+            for (int i = 0; i < _elements.length; i++) {
+                evaluated[i] = _elements[i].evaluate(context);
+            }
+
+            // Determine the common type from the first non-null element
+            Type? common_type = null;
+            foreach (var elem in evaluated) {
+                if (elem.is_null()) continue;
+                
+                Type? elem_type = elem.type();
+                if (elem_type != null) {
+                    // Normalize nullable types to their base types
+                    // int? -> int, double? -> double, etc.
+                    Type normalized_type = normalize_type(elem_type);
+                    
+                    if (common_type == null) {
+                        common_type = normalized_type;
+                    } else if (common_type != normalized_type) {
+                        // Types don't match - fall back to Object
+                        common_type = typeof(Object);
+                        break;
+                    }
+                }
+            }
+
+            // If we couldn't determine a type, use Object
+            if (common_type == null) {
+                common_type = typeof(Object);
+            }
+
+            // Create the appropriate lot based on type
+            if (common_type == typeof(int)) {
+                return create_int_lot(evaluated);
+            } else if (common_type == typeof(double)) {
+                return create_double_lot(evaluated);
+            } else if (common_type == typeof(bool)) {
+                return create_bool_lot(evaluated);
+            } else if (common_type == typeof(string)) {
+                return create_string_lot(evaluated);
+            } else if (common_type == typeof(long)) {
+                return create_long_lot(evaluated);
+            } else {
+                // Fall back to object lot
+                return create_object_lot(evaluated);
+            }
+        }
+
+        private Element create_int_lot(Element[] evaluated) throws ExpressionError {
+            var series = new Series<int>();
+            for (int i = 0; i < evaluated.length; i++) {
+                var elem = evaluated[i];
+                var elem_type = elem.type();
+                
+                // Handle based on the element's actual type
+                if (elem_type == typeof(int) || elem_type == typeof(int?)) {
+                    int? nullable_val;
+                    if (elem.try_get_as<int?>(out nullable_val)) {
+                        series.add(nullable_val);
+                        continue;
+                    }
+                    int val;
+                    if (elem.try_get_as<int>(out val)) {
+                        series.add(val);
+                        continue;
+                    }
+                } else if (elem_type == typeof(long) || elem_type == typeof(long?)) {
+                    long? nullable_val;
+                    if (elem.try_get_as<long?>(out nullable_val)) {
+                        series.add((int)nullable_val);
+                        continue;
+                    }
+                    long val;
+                    if (elem.try_get_as<long>(out val)) {
+                        series.add((int)val);
+                        continue;
+                    }
+                } else if (elem_type == typeof(int64) || elem_type == typeof(int64?)) {
+                    int64? nullable_val;
+                    if (elem.try_get_as<int64?>(out nullable_val)) {
+                        series.add((int)nullable_val);
+                        continue;
+                    }
+                    // int64 can't be used as generic type argument directly
+                    // Try getting as long instead
+                    long long_val;
+                    if (elem.try_get_as<long>(out long_val)) {
+                        series.add((int)long_val);
+                        continue;
+                    }
+                }
+                
+                if (elem.is_null()) {
+                    throw new ExpressionError.TYPE_MISMATCH("Cannot add null to int lot");
+                }
+                
+                throw new ExpressionError.TYPE_MISMATCH(
+                    @"Element $(i) is not an int (got $(elem_type.name()))"
+                );
+            }
+            return new NativeElement<Enumerable<int>>(series);
+        }
+
+        private Element create_double_lot(Element[] evaluated) throws ExpressionError {
+            var series = new Series<double?>();
+            for (int i = 0; i < evaluated.length; i++) {
+                double? dval;
+                int ival;
+                int? nullable_ival;
+                if (evaluated[i].try_get_as<double?>(out dval)) {
+                    series.add(dval);
+                } else if (evaluated[i].try_get_as<int>(out ival)) {
+                    // Promote int to double
+                    series.add((double)ival);
+                } else if (evaluated[i].try_get_as<int?>(out nullable_ival)) {
+                    // Promote int? to double
+                    series.add((double)nullable_ival);
+                } else if (evaluated[i].is_null()) {
+                    throw new ExpressionError.TYPE_MISMATCH("Cannot add null to double lot");
+                } else {
+                    throw new ExpressionError.TYPE_MISMATCH(
+                        @"Element $(i) is not a double"
+                    );
+                }
+            }
+            return new NativeElement<Enumerable<double?>>(series);
+        }
+
+        private Element create_bool_lot(Element[] evaluated) throws ExpressionError {
+            var series = new Series<bool>();
+            for (int i = 0; i < evaluated.length; i++) {
+                bool val;
+                bool? nullable_val;
+                if (evaluated[i].try_get_as<bool>(out val)) {
+                    series.add(val);
+                } else if (evaluated[i].try_get_as<bool?>(out nullable_val)) {
+                    series.add(nullable_val);
+                } else if (evaluated[i].is_null()) {
+                    throw new ExpressionError.TYPE_MISMATCH("Cannot add null to bool lot");
+                } else {
+                    throw new ExpressionError.TYPE_MISMATCH(
+                        @"Element $(i) is not a bool"
+                    );
+                }
+            }
+            return new NativeElement<Enumerable<bool>>(series);
+        }
+
+        private Element create_string_lot(Element[] evaluated) throws ExpressionError {
+            var series = new Series<string?>();
+            for (int i = 0; i < evaluated.length; i++) {
+                string? val;
+                if (evaluated[i].try_get_as<string?>(out val)) {
+                    series.add(val);
+                } else if (evaluated[i].is_null()) {
+                    series.add(null);  // Strings can be null
+                } else {
+                    throw new ExpressionError.TYPE_MISMATCH(
+                        @"Element $(i) is not a string"
+                    );
+                }
+            }
+            return new NativeElement<Enumerable<string?>>(series);
+        }
+
+        private Element create_long_lot(Element[] evaluated) throws ExpressionError {
+            var series = new Series<long>();
+            for (int i = 0; i < evaluated.length; i++) {
+                long lval;
+                long? nullable_lval;
+                int ival;
+                int? nullable_ival;
+                if (evaluated[i].try_get_as<long>(out lval)) {
+                    series.add(lval);
+                } else if (evaluated[i].try_get_as<long?>(out nullable_lval)) {
+                    series.add(nullable_lval);
+                } else if (evaluated[i].try_get_as<int>(out ival)) {
+                    // Promote int to long
+                    series.add((long)ival);
+                } else if (evaluated[i].try_get_as<int?>(out nullable_ival)) {
+                    // Promote int? to long
+                    series.add((long)nullable_ival);
+                } else if (evaluated[i].is_null()) {
+                    throw new ExpressionError.TYPE_MISMATCH("Cannot add null to long lot");
+                } else {
+                    throw new ExpressionError.TYPE_MISMATCH(
+                        @"Element $(i) is not a long"
+                    );
+                }
+            }
+            return new NativeElement<Enumerable<long>>(series);
+        }
+
+        private Element create_object_lot(Element[] evaluated) throws ExpressionError {
+            var series = new Series<Object?>();
+            for (int i = 0; i < evaluated.length; i++) {
+                Object? val;
+                if (evaluated[i].try_get_as<Object?>(out val)) {
+                    series.add(val);
+                } else if (evaluated[i].is_null()) {
+                    series.add(null);
+                } else {
+                    series.add(null);
+                }
+            }
+            return new NativeElement<Enumerable<Object?>>(series);
+        }
+
+        public void accept(ExpressionVisitor visitor) {
+            visitor.visit_lot_literal(this);
+            foreach (var elem in _elements) {
+                elem.accept(visitor);
+            }
+        }
+
+        public override string to_expression_string() {
+            var builder = new StringBuilder();
+            builder.append("[");
+            for (int i = 0; i < _elements.length; i++) {
+                if (i > 0) {
+                    builder.append(", ");
+                }
+                builder.append(_elements[i].to_expression_string());
+            }
+            builder.append("]");
+            return builder.str;
+        }
+
+        /**
+         * Normalizes a type by removing nullable wrapper.
+         * int? -> int, double? -> double, etc.
+         */
+        private Type normalize_type(Type t) {
+            if (t == typeof(int?)) return typeof(int);
+            if (t == typeof(double?)) return typeof(double);
+            if (t == typeof(bool?)) return typeof(bool);
+            if (t == typeof(long?)) return typeof(long);
+            if (t == typeof(string?)) return typeof(string);
+            return t;
+        }
+    }
+}

+ 218 - 0
src/lib/Expressions/IterateFunctionAccessor.vala

@@ -0,0 +1,218 @@
+using Invercargill.DataStructures;
+
+namespace Invercargill.Expressions {
+
+    /**
+     * Function accessor for the Iterate namespace functions.
+     * 
+     * This provides access to generator functions from the Iterate namespace
+     * that can be used within expressions. Supported functions:
+     * 
+     * - `range(from, to, increment?)` - Creates a range of integers
+     * - `single(value)` - Creates an enumerable with a single element
+     * - `nothing()` - Creates an empty enumerable
+     * 
+     * Examples:
+     * - `range(1, 10)` - Creates enumerable [1, 2, 3, 4, 5, 6, 7, 8, 9]
+     * - `range(0, 100, 10)` - Creates enumerable [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
+     * - `single(42)` - Creates enumerable [42]
+     * - `nothing()` - Creates empty enumerable
+     * 
+     * Note: The `directory` and `on` and `deferred` functions are not supported
+     * as they require file system access or delegate handling which is not
+     * suitable for expression evaluation.
+     */
+    public class IterateFunctionAccessor : Object, FunctionAccessor {
+
+        /**
+         * The type this accessor handles.
+         */
+        public Type handled_type {
+            get { return typeof(Object); }  // Acts as a global accessor
+        }
+
+        /**
+         * Gets the names of all available functions.
+         */
+        public Enumerable<string> get_function_names() {
+            var names = new Series<string>();
+            names.add("range");
+            names.add("single");
+            names.add("nothing");
+            return names;
+        }
+
+        /**
+         * Checks if a function is available.
+         */
+        public bool has_function(string name) {
+            return name == "range" || name == "single" || name == "nothing";
+        }
+
+        /**
+         * Calls a function with the given arguments.
+         * 
+         * @param function_name The function name
+         * @param arguments The function arguments as a Series
+         * @param context The evaluation context
+         * @return The result of the function call
+         * @throws ExpressionError if the function doesn't exist or arguments are invalid
+         */
+        public Element call_function(string function_name, Series<Element> arguments, EvaluationContext context) throws ExpressionError {
+            // Convert Series to array for easier access
+            var args = arguments.to_array();
+            
+            switch (function_name) {
+                case "range":
+                    return call_range(args);
+                case "single":
+                    return call_single(args);
+                case "nothing":
+                    return call_nothing(args);
+                default:
+                    throw new ExpressionError.FUNCTION_NOT_FOUND(
+                        @"Unknown Iterate function '$function_name'"
+                    );
+            }
+        }
+
+        /**
+         * Calls the range function.
+         * 
+         * Signature: range(from: int, to: int, increment: int = 1) -> Enumerable<int>
+         */
+        private Element call_range(Element[] args) throws ExpressionError {
+            if (args.length < 2 || args.length > 3) {
+                throw new ExpressionError.INVALID_ARGUMENTS(
+                    @"range() requires 2-3 arguments, got $(args.length)"
+                );
+            }
+
+            // Get 'from' argument
+            int from;
+            if (!try_get_int(args[0], out from)) {
+                throw new ExpressionError.TYPE_MISMATCH(
+                    "range() argument 'from' must be an integer"
+                );
+            }
+
+            // Get 'to' argument
+            int to;
+            if (!try_get_int(args[1], out to)) {
+                throw new ExpressionError.TYPE_MISMATCH(
+                    "range() argument 'to' must be an integer"
+                );
+            }
+
+            // Get optional 'increment' argument
+            int increment = 1;
+            if (args.length == 3) {
+                if (!try_get_int(args[2], out increment)) {
+                    throw new ExpressionError.TYPE_MISMATCH(
+                        "range() argument 'increment' must be an integer"
+                    );
+                }
+            }
+
+            // Create the range
+            var range = Iterate.range(from, to, increment);
+            return new NativeElement<Enumerable<int>>(range);
+        }
+
+        /**
+         * Calls the single function.
+         * 
+         * Signature: single<T>(value: T) -> Enumerable<T>
+         * 
+         * Note: Since we don't have full type information at runtime,
+         * this creates an Enumerable<Object?> and wraps the value.
+         */
+        private Element call_single(Element[] args) throws ExpressionError {
+            if (args.length != 1) {
+                throw new ExpressionError.INVALID_ARGUMENTS(
+                    @"single() requires exactly 1 argument, got $(args.length)"
+                );
+            }
+
+            var arg = args[0];
+
+            if (arg.is_null()) {
+                // Null value - create Object? enumerable
+                var single = Iterate.single<Object?>(null);
+                return new NativeElement<Enumerable<Object?>>(single);
+            }
+
+            // Determine the type and create appropriate enumerable
+            int int_val;
+            long long_val;
+            double double_val;
+            bool bool_val;
+            string string_val;
+            Object object_val;
+
+            if (arg.try_get_as<int>(out int_val)) {
+                var single = Iterate.single<int>(int_val);
+                return new NativeElement<Enumerable<int>>(single);
+            } else if (arg.try_get_as<long>(out long_val)) {
+                var single = Iterate.single<long>(long_val);
+                return new NativeElement<Enumerable<long>>(single);
+            } else if (arg.try_get_as<double?>(out double_val)) {
+                var single = Iterate.single<double?>(double_val);
+                return new NativeElement<Enumerable<double?>>(single);
+            } else if (arg.try_get_as<bool>(out bool_val)) {
+                var single = Iterate.single<bool>(bool_val);
+                return new NativeElement<Enumerable<bool>>(single);
+            } else if (arg.try_get_as<string?>(out string_val)) {
+                var single = Iterate.single<string?>(string_val);
+                return new NativeElement<Enumerable<string?>>(single);
+            } else if (arg.try_get_as<Object?>(out object_val)) {
+                var single = Iterate.single<Object?>(object_val);
+                return new NativeElement<Enumerable<Object?>>(single);
+            } else {
+                // Unknown type - wrap as null object
+                var single = Iterate.single<Object?>(null);
+                return new NativeElement<Enumerable<Object?>>(single);
+            }
+        }
+
+        /**
+         * Calls the nothing function.
+         * 
+         * Signature: nothing<T>() -> Enumerable<T>
+         * 
+         * Note: Returns an empty Enumerable<Object?> since we don't have
+         * type parameters in expressions.
+         */
+        private Element call_nothing(Element[] args) throws ExpressionError {
+            if (args.length != 0) {
+                throw new ExpressionError.INVALID_ARGUMENTS(
+                    @"nothing() takes no arguments, got $(args.length)"
+                );
+            }
+
+            var nothing = Iterate.nothing<Object?>();
+            return new NativeElement<Enumerable<Object?>>(nothing);
+        }
+
+        /**
+         * Helper to try getting an int from an element.
+         */
+        private bool try_get_int(Element elem, out int result) {
+            result = 0;
+            
+            int int_val;
+            if (elem.try_get_as<int>(out int_val)) {
+                result = int_val;
+                return true;
+            }
+            
+            long long_val;
+            if (elem.try_get_as<long>(out long_val)) {
+                result = (int)long_val;
+                return true;
+            }
+            
+            return false;
+        }
+    }
+}

+ 3 - 0
src/lib/meson.build

@@ -152,6 +152,7 @@ sources += files('Expressions/FunctionAccessor.vala')
 sources += files('Expressions/BinaryOperator.vala')
 sources += files('Expressions/UnaryOperator.vala')
 sources += files('Expressions/EnumerableFunctionAccessor.vala')
+sources += files('Expressions/IterateFunctionAccessor.vala')
 sources += files('Expressions/ExpressionTokenizer.vala')
 sources += files('Expressions/ExpressionParser.vala')
 
@@ -164,8 +165,10 @@ sources += files('Expressions/Expressions/PropertyExpression.vala')
 sources += files('Expressions/Expressions/FunctionCallExpression.vala')
 sources += files('Expressions/Expressions/LambdaExpression.vala')
 sources += files('Expressions/Expressions/BracketedExpression.vala')
+sources += files('Expressions/Expressions/LotLiteralExpression.vala')
 
 sources += files('Expressions/Elements/LambdaElement.vala')
+sources += files('Expressions/Elements/GlobalFunctionsElement.vala')
 
 invercargill = shared_library('invercargill-@0@'.format(invercargill_major), sources,
     dependencies: dependencies,

+ 200 - 0
src/tests/Integration/Expressions.vala

@@ -945,4 +945,204 @@ void expression_tests() {
         assert(result.try_get_as<bool?>(out value));
         assert(value == true);
     });
+
+    // ==================== Lot Literal Tests ====================
+
+    Test.add_func("/invercargill/expressions/lot_literal_empty", () => {
+        var expr = new LotLiteralExpression(new Expression[0]);
+        var props = new PropertyDictionary();
+        var context = new EvaluationContext(props);
+        
+        var result = expr.evaluate(context);
+        assert(result != null);
+        
+        Enumerable<Object?>? lot;
+        assert(result.try_get_as<Enumerable<Object?>>(out lot));
+        assert(lot != null);
+        
+        var count = 0;
+        foreach (var item in lot) {
+            count++;
+        }
+        assert(count == 0);
+    });
+
+    Test.add_func("/invercargill/expressions/lot_literal_integers", () => {
+        var elements = new Expression[3];
+        elements[0] = new LiteralExpression(new NativeElement<int?>(1));
+        elements[1] = new LiteralExpression(new NativeElement<int?>(2));
+        elements[2] = new LiteralExpression(new NativeElement<int?>(3));
+        
+        var expr = new LotLiteralExpression(elements);
+        var props = new PropertyDictionary();
+        var context = new EvaluationContext(props);
+        
+        var result = expr.evaluate(context);
+        assert(result != null);
+        
+        Enumerable<int>? lot;
+        assert(result.try_get_as<Enumerable<int>?>(out lot));
+        assert(lot != null);
+        
+        var values = new Series<int>();
+        foreach (var item in lot) {
+            values.add(item);
+        }
+        
+        assert(values.length == 3);
+        
+        var idx = 0;
+        foreach (var val in values) {
+            if (idx == 0) assert(val == 1);
+            else if (idx == 1) assert(val == 2);
+            else if (idx == 2) assert(val == 3);
+            idx++;
+        }
+    });
+
+    Test.add_func("/invercargill/expressions/lot_literal_strings", () => {
+        var elements = new Expression[3];
+        elements[0] = new LiteralExpression(new NativeElement<string?>("a"));
+        elements[1] = new LiteralExpression(new NativeElement<string?>("b"));
+        elements[2] = new LiteralExpression(new NativeElement<string?>("c"));
+        
+        var expr = new LotLiteralExpression(elements);
+        var props = new PropertyDictionary();
+        var context = new EvaluationContext(props);
+        
+        var result = expr.evaluate(context);
+        assert(result != null);
+        
+        Enumerable<string?>? lot;
+        assert(result.try_get_as<Enumerable<string?>>(out lot));
+        assert(lot != null);
+        
+        var values = new Series<string?>();
+        foreach (var item in lot) {
+            values.add(item);
+        }
+        
+        assert(values.length == 3);
+    });
+
+    Test.add_func("/invercargill/expressions/lot_literal_parser", () => {
+        var expr = ExpressionParser.parse("[1, 2, 3]");
+        var props = new PropertyDictionary();
+        var context = new EvaluationContext(props);
+        
+        var result = expr.evaluate(context);
+        assert(result != null);
+        
+        Enumerable<int>? lot;
+        assert(result.try_get_as<Enumerable<int>?>(out lot));
+        assert(lot != null);
+        
+        var count = 0;
+        foreach (var item in lot) {
+            count++;
+        }
+        assert(count == 3);
+    });
+
+    // ==================== Iterate Function Tests ====================
+
+    Test.add_func("/invercargill/expressions/iterate_range", () => {
+        var accessor = new IterateFunctionAccessor();
+        var args = new Series<Element>();
+        args.add(new NativeElement<int>(0));
+        args.add(new NativeElement<int>(5));
+        
+        var result = accessor.call_function("range", args, new EvaluationContext(new PropertyDictionary()));
+        assert(result != null);
+        
+        Enumerable<int>? range;
+        assert(result.try_get_as<Enumerable<int>?>(out range));
+        assert(range != null);
+        
+        var values = new Series<int>();
+        foreach (var item in range) {
+            values.add(item);
+        }
+        
+        assert(values.length == 5);
+    });
+
+    Test.add_func("/invercargill/expressions/iterate_single", () => {
+        var accessor = new IterateFunctionAccessor();
+        var args = new Series<Element>();
+        args.add(new NativeElement<int>(42));
+        
+        var result = accessor.call_function("single", args, new EvaluationContext(new PropertyDictionary()));
+        assert(result != null);
+        
+        Enumerable<int>? single;
+        assert(result.try_get_as<Enumerable<int>?>(out single));
+        assert(single != null);
+        
+        var count = 0;
+        foreach (var item in single) {
+            assert(item == 42);
+            count++;
+        }
+        assert(count == 1);
+    });
+
+    Test.add_func("/invercargill/expressions/iterate_nothing", () => {
+        var accessor = new IterateFunctionAccessor();
+        var args = new Series<Element>();
+        
+        var result = accessor.call_function("nothing", args, new EvaluationContext(new PropertyDictionary()));
+        assert(result != null);
+        
+        Enumerable<Object?>? empty;
+        assert(result.try_get_as<Enumerable<Object?>>(out empty));
+        assert(empty != null);
+        
+        var count = 0;
+        foreach (var item in empty) {
+            count++;
+        }
+        assert(count == 0);
+    });
+
+    Test.add_func("/invercargill/expressions/iterate_range_with_step", () => {
+        var accessor = new IterateFunctionAccessor();
+        var args = new Series<Element>();
+        args.add(new NativeElement<int>(0));
+        args.add(new NativeElement<int>(10));
+        args.add(new NativeElement<int>(2));
+        
+        var result = accessor.call_function("range", args, new EvaluationContext(new PropertyDictionary()));
+        assert(result != null);
+        
+        Enumerable<int>? range;
+        assert(result.try_get_as<Enumerable<int>?>(out range));
+        assert(range != null);
+        
+        var values = new Series<int>();
+        foreach (var item in range) {
+            values.add(item);
+        }
+        
+        assert(values.length == 5); // 0, 2, 4, 6, 8
+    });
+
+    Test.add_func("/invercargill/expressions/global_functions_element", () => {
+        var accessor = new IterateFunctionAccessor();
+        var global_element = new GlobalFunctionsElement(accessor);
+        
+        var props = new PropertyDictionary();
+        props.set("Iterate", global_element);
+        var context = new EvaluationContext(props);
+        
+        // Get the variable
+        var expr = new VariableExpression("Iterate");
+        var result = expr.evaluate(context);
+        assert(result != null);
+        
+        GlobalFunctionsElement? retrieved;
+        assert(result.try_get_as<GlobalFunctionsElement?>(out retrieved));
+        assert(retrieved != null);
+        assert(retrieved.accessor is IterateFunctionAccessor);
+    });
 }