toSExpression method

String toSExpression({
  1. bool includeAll = false,
})

Native ts_subtree_string representation.

includeAll corresponds to the native helper's internal include_all flag. The ordinary public tree/node string path leaves it false.

Implementation

String toSExpression({bool includeAll = false}) {
  final frames = <_GeneratedSExpressionFrame>[
    _GeneratedSExpressionFrame(this, root: true, includeAll: includeAll),
  ];
  var result = '';
  while (frames.isNotEmpty) {
    final frame = frames.last;
    final node = frame.node;
    if (frame.nextChild < node.children.length) {
      final child = node.children[frame.nextChild++];
      String? childFieldName;
      if (!child.extra) {
        childFieldName = frame.isVisible ? null : frame.fieldName;
        for (final field in node.productionFields) {
          if (!field.inherited && field.childIndex == frame.structuralIndex) {
            childFieldName = field.name;
            break;
          }
        }
        if (childFieldName == null) {
          final directFields = child.fields.where(
            (field) => !field.inherited,
          );
          if (directFields.isNotEmpty) {
            childFieldName = directFields.first.name;
          }
        }
        frame.structuralIndex++;
      }
      frames.add(
        _GeneratedSExpressionFrame(
          child,
          fieldName: childFieldName,
          root: !frame.isVisible && frame.root,
          includeAll: includeAll,
        ),
      );
      continue;
    }

    final unexpected = node.unexpectedCharacter;
    String? head;
    if (unexpected != null) {
      final value = switch (unexpected) {
        -1 => 'INVALID',
        0 => r"'\0'",
        0x0a => r"'\n'",
        0x09 => r"'\t'",
        0x0d => r"'\r'",
        >= 0x20 && < 0x7f => "'${String.fromCharCode(unexpected)}'",
        _ => '$unexpected',
      };
      head = '(UNEXPECTED $value';
    } else if (node.missing) {
      final name = node.missingNameIsNamed
          ? node.type
          : jsonEncode(node.type);
      head = '(MISSING $name';
    } else if (frame.isVisible) {
      head = '(${node.type}';
    }

    String value;
    if (frame.isVisible) {
      value =
          '${frame.root || frame.fieldName == null ? '' : '${frame.fieldName}: '}'
          '$head${frame.children.isEmpty ? '' : ' ${frame.children.join(' ')}'})';
    } else if (frame.root) {
      if (node.children.isNotEmpty) {
        // This intentionally mirrors the hidden-root branch in
        // `ts_subtree__write_to_string`, including its lack of a closing
        // parenthesis when the hidden root has children.
        value = '(${node.type}${frame.children.join()}';
      } else if (node.named) {
        value = '(${node.type})';
      } else {
        value = '(${jsonEncode(node.type)})';
      }
    } else {
      value = frame.children.join(' ');
    }
    frames.removeLast();
    if (frames.isEmpty) {
      result = value;
    } else if (value.isNotEmpty) {
      frames.last.children.add(value);
    }
  }
  return result;
}