memberAccess function

Parser memberAccess()

Member access parser - handles dot notation like product.name.first.

Optimized with a two-tier approach:

  1. Fast path: Pattern-based parsing for simple identifier chains (no array access) Uses a single pattern match + string split instead of parsing each identifier
  2. Slow path: Original recursive parsing for complex cases with array access

The fast path provides ~50% reduction in parser activations for simple cases.

Implementation

Parser memberAccess() {
  // Fast path: Simple identifier chain without array access
  // Pattern matches: identifier(.identifier)+ where no segment is followed by '['
  final identChar = pattern('a-zA-Z0-9_-');
  final dot = char('.');

  // First segment: identifier pattern
  final firstIdent = pattern('a-zA-Z') & identChar.star();

  // Additional segments: .identifier (NOT followed by '[')
  final additionalSegment = dot & pattern('a-zA-Z') & identChar.star();

  // Fast path parser - matches simple chains and ensures not followed by '['
  // The negative lookahead char('[').not() ensures we don't match when array access follows
  final simpleMemberAccess =
      ((firstIdent & additionalSegment.plus()).flatten() & char('[').not())
          .pick(0) // Get just the flattened string, not the lookahead result
          .cast<String>()
          .where((value) {
            // Exclude reserved words in any segment
            final parts = value.split('.');
            return !parts.any(
              (p) => ['and', 'or', 'not', 'contains'].contains(p),
            );
          })
          .map((value) {
            final parts = value.split('.');
            final object = Identifier(parts.first);
            final members = parts
                .skip(1)
                .map((name) => Identifier(name))
                .toList();
            return MemberAccess(object, members.cast<ASTNode>());
          });

  // Slow path: Original recursive parsing for complex cases (with array access)
  final complexMemberAccess =
      (ref0(identifier) &
              (char('.') & (ref0(arrayAccess) | ref0(identifier))).plus())
          .map((values) {
            var object = values[0] as Identifier;
            var members = (values[1] as List)
                .map((m) => m[1] as ASTNode)
                .toList();
            return MemberAccess(object, members);
          });

  // Try fast path first, fall back to slow path
  return (simpleMemberAccess | complexMemberAccess).labeled('memberAccess');
}