smart_parser 2.2.0 copy "smart_parser: ^2.2.0" to clipboard
smart_parser: ^2.2.0 copied to clipboard

All in one, a generator of recursive descent PEG parsers, tokenizers, and token stream parsers.

example/example.dart

import 'package:source_span/source_span.dart';

Object? parse(String source) {
  const parser = JsonParser();
  final state = State(source);
  final result = parser.parseStart(state);
  if (result == null) {
    final file = SourceFile.fromString(source);
    final message = state
        .getErrors()
        .map((e) => file.span(e.start, e.end).message(e.message))
        .join('\n');
    throw FormatException('\n$message');
  }

  return result.$1;
}

// dart format off
class JsonParser {
  const JsonParser();

  /// [Object?] **Start**
  /// ```txt
  /// `Object?` Start =>
  ///   S
  ///   $ = Value
  ///   & { state.ch < 0 }
  ///   ~{ state.errorExpected('enf of file'); }
  /// ```
  Result<Object?>? parseStart(State state) {
    final pos = state.position;
    final ch1 = state.ch;
    parseS(state);
    l:
    {
      final value = parseValue(state);
      if (value != null) {
        final isSuccess = state.ch < 0;
        if (isSuccess) {
          return value;
        }
        state.errorExpected('enf of file');
        break l;
      }
      break l;
    }
    // l:
    state.ch = ch1;
    state.position = pos;
    return null;
  }

  /// [List<Object?>] **Elements**
  /// ```txt
  /// `List<Object?>` Elements =>
  ///   value = Value
  ///   { final elements = [value]; }
  ///   @while (0) {
  ///     ',' S
  ///     value = Value
  ///     { elements.add(value); }
  ///   }
  ///   $ = { elements }
  /// ```
  Result<List<Object?>>? parseElements(State state) {
    final value1 = parseValue(state);
    if (value1 != null) {
      final value = value1.$1;
      final elements = [value];
      // (0)
      while (true) {
        // ','
        if (state.ch == 44) {
          final pos = state.position;
          final ch = state.ch;
          state.nextChar();
          parseS(state);
          final value2 = parseValue(state);
          if (value2 != null) {
            final value = value2.$1;
            elements.add(value);
            continue;
          }
          state.ch = ch;
          state.position = pos;
          break;
        }
        state.errorExpected(',');
        break;
      }
      return Ok(elements);
    }
    return null;
  }

  /// [List<Object?>] **Array**
  /// ```txt
  /// `List<Object?>` Array =>
  ///   "[" S
  ///   elements = Elements?
  ///   ']' S
  ///   $ = { elements ?? [] }
  /// ```
  Result<List<Object?>>? parseArray(State state) {
    // "["
    if (state.ch == 91) {
      final pos = state.position;
      final ch = state.ch;
      state.nextChar();
      parseS(state);
      final elements = parseElements(state)?.$1;
      // ']'
      if (state.ch == 93) {
        state.nextChar();
        parseS(state);
        return Ok(elements ?? []);
      }
      state.errorExpected(']');
      state.ch = ch;
      state.position = pos;
      return null;
    }
    return null;
  }

  /// [MapEntry<String, Object?>] **KeyValue**
  /// ```txt
  /// `MapEntry<String, Object?>` KeyValue =>
  ///   key = String
  ///   ~{ state.errorExpected('string'); }
  ///   ':' S
  ///   value = Value
  ///   $ = { MapEntry(key, value) }
  /// ```
  Result<MapEntry<String, Object?>>? parseKeyValue(State state) {
    final pos = state.position;
    final ch = state.ch;
    final string1 = parseString(state);
    if (string1 != null) {
      final key = string1.$1;
      l:
      {
        // ':'
        if (state.ch == 58) {
          state.nextChar();
          parseS(state);
          final value1 = parseValue(state);
          if (value1 != null) {
            final value = value1.$1;
            return Ok(MapEntry(key, value));
          }
          break l;
        }
        state.errorExpected(':');
        break l;
      }
      // l:
      state.ch = ch;
      state.position = pos;
      return null;
    }
    state.errorExpected('string');
    return null;
  }

  /// [Map<String, Object?>] **Map**
  /// ```txt
  /// `Map<String, Object?>` Map =>
  ///   keyValue = KeyValue
  ///   {
  ///     final map = <String, Object?>{};
  ///     map[keyValue.key] = keyValue.value;
  ///   }
  ///   @while (0) {
  ///     ',' S
  ///     keyValue = KeyValue
  ///     { map[keyValue.key] = keyValue.value; }
  ///   }
  ///   $ = { map }
  /// ```
  Result<Map<String, Object?>>? parseMap(State state) {
    final keyValue1 = parseKeyValue(state);
    if (keyValue1 != null) {
      final keyValue = keyValue1.$1;
      final map = <String, Object?>{};
      map[keyValue.key] = keyValue.value;
      // (0)
      while (true) {
        // ','
        if (state.ch == 44) {
          final pos = state.position;
          final ch = state.ch;
          state.nextChar();
          parseS(state);
          final keyValue2 = parseKeyValue(state);
          if (keyValue2 != null) {
            final keyValue = keyValue2.$1;
            map[keyValue.key] = keyValue.value;
            continue;
          }
          state.ch = ch;
          state.position = pos;
          break;
        }
        state.errorExpected(',');
        break;
      }
      return Ok(map);
    }
    return null;
  }

  /// [Map<String, Object?>] **Object**
  /// ```txt
  /// `Map<String, Object?>` Object =>
  ///   "{" S
  ///   map = Map?
  ///   '}' S
  ///   $ = { map ?? {} }
  /// ```
  Result<Map<String, Object?>>? parseObject(State state) {
    // "{"
    if (state.ch == 123) {
      final pos = state.position;
      final ch = state.ch;
      state.nextChar();
      parseS(state);
      final map = parseMap(state)?.$1;
      // '}'
      if (state.ch == 125) {
        state.nextChar();
        parseS(state);
        return Ok(map ?? {});
      }
      state.errorExpected('}');
      state.ch = ch;
      state.position = pos;
      return null;
    }
    return null;
  }

  /// [String] **EscapeC**
  /// ```txt
  /// `String` EscapeC =>
  ///   (
  ///     ["]
  ///     $ = `const` { '"' }
  ///     ----
  ///     [\\]
  ///     $ = `const` { '\\' }
  ///     ----
  ///     [/]
  ///     $ = `const` { '/' }
  ///     ----
  ///     [b]
  ///     $ = `const` { '\b' }
  ///     ----
  ///     [f]
  ///     $ = `const` { '\f' }
  ///     ----
  ///     [n]
  ///     $ = `const` { '\n' }
  ///     ----
  ///     [r]
  ///     $ = `const` { '\r' }
  ///     ----
  ///     [t]
  ///     $ = `const` { '\t' }
  ///   )
  ///   ~{
  ///     if (state.position == state.length) {
  ///       state.errorExpected('escape character');
  ///     } else {
  ///       state.error('Illegal escape character');
  ///     }
  ///   }
  /// ```
  Result<String>? parseEscapeC(State state) {
    // ["]
    if (state.ch == 34) {
      state.nextChar();
      return const Ok('"');
    }
    // [\\]
    if (state.ch == 92) {
      state.nextChar();
      return const Ok('\\');
    }
    // [/]
    if (state.ch == 47) {
      state.nextChar();
      return const Ok('/');
    }
    // [b]
    if (state.ch == 98) {
      state.nextChar();
      return const Ok('\b');
    }
    // [f]
    if (state.ch == 102) {
      state.nextChar();
      return const Ok('\f');
    }
    // [n]
    if (state.ch == 110) {
      state.nextChar();
      return const Ok('\n');
    }
    // [r]
    if (state.ch == 114) {
      state.nextChar();
      return const Ok('\r');
    }
    // [t]
    if (state.ch == 116) {
      state.nextChar();
      return const Ok('\t');
    }
    if (state.position == state.length) {
      state.errorExpected('escape character');
    } else {
      state.error('Illegal escape character');
    }
    return null;
  }

  /// [String] **EscapeUnicode**
  /// ```txt
  /// `String` EscapeUnicode =>
  ///   { final start = state.position; }
  ///   "u"
  ///   { var end = 0; }
  ///   text = <
  ///     @while (4, 4) {
  ///       [a-fA-F0-9]
  ///       ~{
  ///         end = state.position;
  ///         state.errorExpected('hexadecimal digit');
  ///       }
  ///     }
  ///   >
  ///   ~{ state.error('Incorrect Unicode escape sequence', position: end, start: start, end: end); }
  ///   $ = { String.fromCharCode(int.parse(text, radix: 16)) }
  /// ```
  Result<String>? parseEscapeUnicode(State state) {
    final start = state.position;
    // "u"
    if (state.ch == 117) {
      final pos = state.position;
      final ch = state.ch;
      state.nextChar();
      var end = 0;
      final start1 = state.position;
      final pos1 = state.position;
      final ch1 = state.ch;
      var count = 0;
      // (4, 4)
      while (count < 4) {
        // [a-fA-F0-9]
        final c = state.ch;
        final isHexDigit = c <= 70 ? c >= 65 || c >= 48 && c <= 57 : c >= 97 && c <= 102;
        if (isHexDigit) {
          state.nextChar();
          count++;
          continue;
        }
        end = state.position;
        state.errorExpected('hexadecimal digit');
        break;
      }
      if (count >= 4) {
        final text = state.substring(start1, state.position);
        return Ok(String.fromCharCode(int.parse(text, radix: 16)));
      } else {
        state.ch = ch1;
        state.position = pos1;
        state.error('Incorrect Unicode escape sequence', position: end, start: start, end: end);
        state.ch = ch;
        state.position = pos;
        return null;
      }
    }
    return null;
  }

  /// [String] **Escaped**
  /// ```txt
  /// `String` Escaped =>
  ///   & "u"
  ///   $ = EscapeUnicode
  ///   ----
  ///   EscapeC
  /// ```
  Result<String>? parseEscaped(State state) {
    // "u"
    if (state.ch == 117) {
      final escapeUnicode = parseEscapeUnicode(state);
      if (escapeUnicode != null) {
        return escapeUnicode;
      }
    }
    final escapeC = parseEscapeC(state);
    if (escapeC != null) {
      return escapeC;
    }
    return null;
  }

  /// [String] **String**
  /// ```txt
  /// `String` String =>
  ///   { final start = state.position; }
  ///   ["]
  ///   parts = @while (0) {
  ///     <[^{0-1F}"\\]+>
  ///     ---
  ///     [\\]
  ///     $ = Escaped
  ///   }
  ///   ["]
  ///   ~{
  ///     state.error('Unterminated string', start: start);
  ///     state.errorExpected('"');
  ///   }
  ///   S
  ///   $ = { parts.length == 1 ? parts[0] : parts.isNotEmpty ? parts.join() : '' }
  /// ```
  Result<String>? parseString(State state) {
    final start = state.position;
    // ["]
    if (state.ch == 34) {
      final pos = state.position;
      final ch = state.ch;
      state.nextChar();
      final parts1 = <String>[];
      // (0)
      while (true) {
        final start1 = state.position;
        var isSuccess = false;
        // (1)
        while (true) {
          // [^{0-1f}"\\]
          final c = state.ch;
          final isNotBackslashOrControlOrDoubleQuote = !(c <= 34 ? c >= 34 || c >= 0 && c <= 31 : c == 92) && !(c < 0);
          if (isNotBackslashOrControlOrDoubleQuote) {
            state.nextChar();
            isSuccess = true;
            continue;
          }
          break;
        }
        if (isSuccess) {
          parts1.add(state.substring(start1, state.position));
          continue;
        } else {
          // [\\]
          if (state.ch == 92) {
            final pos1 = state.position;
            final ch1 = state.ch;
            state.nextChar();
            final escaped = parseEscaped(state);
            if (escaped != null) {
              parts1.add(escaped.$1);
              continue;
            }
            state.ch = ch1;
            state.position = pos1;
            break;
          }
          break;
        }
      }
      final parts = parts1;
      // ["]
      if (state.ch == 34) {
        state.nextChar();
        parseS(state);
        return Ok(parts.length == 1 ? parts[0] : parts.isNotEmpty ? parts.join() : '');
      }
      state.error('Unterminated string', start: start);
      state.errorExpected('"');
      state.ch = ch;
      state.position = pos;
      return null;
    }
    return null;
  }

  /// [num] **Number**
  /// ```txt
  /// `num` Number =>
  ///   {
  ///     final start = state.position;
  ///     var flag = true;
  ///   }
  ///   [-]?
  ///   ([0] / [1-9] [0-9]*)
  ///   ~{ state.errorExpected('digit'); }
  ///   (
  ///     [.]
  ///     [0-9]+
  ///     ~{
  ///       state.errorExpected('digit');
  ///       state.error('Fractional part is missing a number');
  ///       state.error('Malformed number', start: start, end: state.position);
  ///     }
  ///     { flag = false; }
  ///   )?
  ///   (
  ///     [eE]
  ///     [\-+]?
  ///     [0-9]+
  ///     ~{
  ///       state.errorExpected('digit');
  ///       state.error('Exponent part is missing a number');
  ///       state.error('Malformed number', start: start, end: state.position);
  ///     }
  ///     { flag = false; }
  ///   )?
  ///   text = { state.substring(start, state.position) }
  ///   S
  ///   $ = { flag && text.length <= 18 ? int.parse(text) : num.parse(text) }
  /// ```
  Result<num>? parseNumber(State state) {
    final pos = state.position;
    final ch = state.ch;
    final start = state.position;
    var flag = true;
    // [\-]
    if (state.ch == 45) {
      state.nextChar();
    }
    l:
    {
      // [0]
      if (state.ch == 48) {
        state.nextChar();
        break l;
      }
      // [1-9]
      final c = state.ch;
      final isNonZeroDigit = c >= 49 && c <= 57;
      if (isNonZeroDigit) {
        state.nextChar();
        // (0)
        while (true) {
          // [0-9]
          final c1 = state.ch;
          final isDigit = c1 >= 48 && c1 <= 57;
          if (isDigit) {
            state.nextChar();
            continue;
          }
          break;
        }
        break l;
      }
      state.errorExpected('digit');
      state.ch = ch;
      state.position = pos;
      return null;
    }
    // l:
    l1:
    {
      // [.]
      if (state.ch == 46) {
        final pos1 = state.position;
        final ch1 = state.ch;
        state.nextChar();
        var isSuccess = false;
        // (1)
        while (true) {
          // [0-9]
          final c2 = state.ch;
          final isDigit1 = c2 >= 48 && c2 <= 57;
          if (isDigit1) {
            state.nextChar();
            isSuccess = true;
            continue;
          }
          break;
        }
        if (isSuccess) {
          flag = false;
          break l1;
        } else {
          state.errorExpected('digit');
          state.error('Fractional part is missing a number');
          state.error('Malformed number', start: start, end: state.position);
          state.ch = ch1;
          state.position = pos1;
          break l1;
        }
      }
      break l1;
    }
    // l1:
    l2:
    {
      // [eE]
      final c3 = state.ch;
      final isInRange = c3 == 69 || c3 == 101;
      if (isInRange) {
        final pos2 = state.position;
        final ch2 = state.ch;
        state.nextChar();
        // [\-+]
        final c4 = state.ch;
        final isMinusOrPlus = c4 == 43 || c4 == 45;
        if (isMinusOrPlus) {
          state.nextChar();
        }
        var isSuccess1 = false;
        // (1)
        while (true) {
          // [0-9]
          final c5 = state.ch;
          final isDigit2 = c5 >= 48 && c5 <= 57;
          if (isDigit2) {
            state.nextChar();
            isSuccess1 = true;
            continue;
          }
          break;
        }
        if (isSuccess1) {
          flag = false;
          break l2;
        } else {
          state.errorExpected('digit');
          state.error('Exponent part is missing a number');
          state.error('Malformed number', start: start, end: state.position);
          state.ch = ch2;
          state.position = pos2;
          break l2;
        }
      }
      break l2;
    }
    // l2:
    final text = state.substring(start, state.position);
    parseS(state);
    return Ok(flag && text.length <= 18 ? int.parse(text) : num.parse(text));
  }

  /// [Object?] **Value**
  /// ```txt
  /// `Object?` Value =>
  ///   (
  ///     "null" S
  ///     $ = `const` { null }
  ///     ----
  ///     "true" S
  ///     $ = `const` { true }
  ///     ----
  ///     "false" S
  ///     $ = `const` { false }
  ///     ----
  ///     & "{"
  ///     $ = Object
  ///     ----
  ///     & "["
  ///     $ = Array
  ///     ----
  ///     & ["]
  ///     $ = String
  ///     ----
  ///     Number
  ///   )
  ///   ~{ state.errorExpected(const ['string', 'number', 'array', 'object', 'null', 'boolean value']); }
  /// ```
  Result<Object?>? parseValue(State state) {
    // "null"
    if (state.ch == 110 && state.startsWith('null')) {
      state.readChar(state.position + 4);
      parseS(state);
      return const Ok(null);
    }
    // "true"
    if (state.ch == 116 && state.startsWith('true')) {
      state.readChar(state.position + 4);
      parseS(state);
      return const Ok(true);
    }
    // "false"
    if (state.ch == 102 && state.startsWith('false')) {
      state.readChar(state.position + 5);
      parseS(state);
      return const Ok(false);
    }
    // "{"
    if (state.ch == 123) {
      final object1 = parseObject(state);
      if (object1 != null) {
        return object1;
      }
    }
    // "["
    if (state.ch == 91) {
      final array1 = parseArray(state);
      if (array1 != null) {
        return array1;
      }
    }
    // ["]
    if (state.ch == 34) {
      final string1 = parseString(state);
      if (string1 != null) {
        return string1;
      }
    }
    final number1 = parseNumber(state);
    if (number1 != null) {
      return number1;
    }
    state.errorExpected(const ['string', 'number', 'array', 'object', 'null', 'boolean value']);
    return null;
  }

  /// [void] **S**
  /// ```txt
  /// `void` S =>
  ///     [\n\r\t ]*
  /// ```
  Result<void> parseS(State state) {
    // (0)
    while (true) {
      // [\n\r\t ]
      final c = state.ch;
      final isWhitespace = c <= 13 ? c >= 13 || c >= 9 && c <= 10 : c == 32;
      if (isWhitespace) {
        state.nextChar();
        continue;
      }
      break;
    }
    return Result.none;
  }

}
// dart format on

/// Shortened name (alias) for the [Result] type.
typedef Ok<T> = Result<T>;

class Result<R> {
  /// A successful result that does not provide any resulting value.
  static const none = Result<void>(null);

  // Resulting value.
  final R $1;

  const Result(this.$1);

  @override
  int get hashCode => $1.hashCode;

  @override
  bool operator ==(Object other) => other is Result<R> && other.$1 == $1;

  @override
  String toString() => $1.toString();
}

class State {
  static const _errorExpected = 0;

  static const _maxErrorCount = 64;

  /// Current character.
  int ch = -1;

  /// The furthest position of parsing.
  int farthestPosition = 0;

  /// The length of the input data.
  final int length;

  /// Current parsing position.
  int position = 0;

  /// Intended for internal use only.
  int predicate = 0;

  int _errorCount = 0;

  int _farthestError = 0;

  final List<int?> _flags = List.filled(_maxErrorCount, null);

  final String _input;

  final List<int?> _ends = List.filled(_maxErrorCount, null);

  final List<Object?> _messages = List.filled(_maxErrorCount, null);

  final List<int?> _starts = List.filled(_maxErrorCount, null);

  State(String input) : _input = input, length = input.length {
    readChar(0);
  }

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  int charAt(int position) {
    if (position < length) {
      final ch = _input.codeUnitAt(position);
      if (ch < 0xd800) {
        return ch;
      }

      if (ch < 0xe000) {
        final c = _input.codeUnitAt(position + 1);
        if ((c & 0xfc00) == 0xdc00) {
          return 0x10000 + ((ch & 0x3ff) << 10) + (c & 0x3ff);
        }

        throw FormatException('Invalid UTF-16 character', this, position);
      }

      return ch;
    }

    return -1;
  }

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Returns the size (as the length of the equivalent string) of the character
  /// [char].
  int charSize(int char) => char > 0xffff ? 2 : 1;

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Adds (if possible) an error to the error buffer.
  ///
  /// Parameters:
  ///
  ///  - [message]: A message that describes the error.
  ///  - [position]: Farthest position of the error. Used to determine whether
  /// errors can be added in the error buffer.
  ///  - [start]: Starting position of the location. Used to display the start
  /// of an error.
  ///  - [end]: Ending position of the location. Used to display the end of an
  /// error.
  void error(String message, {int? position, int? start, int? end}) {
    position ??= this.position;
    if (_farthestError <= position) {
      if (_farthestError < position) {
        _farthestError = position;
        _errorCount = 0;
      }

      if (_errorCount < _messages.length) {
        _ends[_errorCount] = end;
        _flags[_errorCount] = null;
        _messages[_errorCount] = message;
        _starts[_errorCount] = start;
        _errorCount++;
      }
    }
  }

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Adds (if possible) an `expected` error to the error buffer.
  ///
  /// Parameters:
  ///
  ///  - [expected]: One or more expected syntactic elements.
  ///  - [position]: Farthest position of the error. Used to determine whether
  /// errors can be added in the error buffer.
  ///  - [start]: Starting position of the location. Used to display the start
  /// of an error.
  ///
  /// Example with one element:
  ///
  /// ```dart
  /// state.errorExpected('string');
  /// ```
  /// Example with multiple elements:
  ///
  /// ```dart
  /// state.errorExpected(const ['string', 'number']);
  /// ```
  void errorExpected(Object expected, {int? position, int? start}) {
    position ??= this.position;
    if (_farthestError <= position) {
      if (_farthestError < position) {
        _farthestError = position;
        _errorCount = 0;
      }

      if (_errorCount < _messages.length) {
        _flags[_errorCount] = _errorExpected;
        _messages[_errorCount] = expected;
        _starts[_errorCount] = start;
        _errorCount++;
      }
    }
  }

  /// Converts error messages to errors and returns them as an error list.
  List<({int end, String message, int start})> getErrors() {
    final position = _farthestError;
    final errors = <({int end, String message, int start})>[];
    final expected = <int, Set<String>>{};
    for (var i = 0; i < _errorCount; i++) {
      final message = _messages[i];
      switch (_flags[i]) {
        case _errorExpected:
          final start = _starts[i] ??= position;
          if (message is List) {
            (expected[start] ??= {}).addAll(message.map((e) => '$e'));
          } else {
            (expected[start] ??= {}).add('$message');
          }

          break;
        default:
          var start = _starts[i];
          var end = _ends[i];
          if (end == null && start != null) {
            end = start;
          } else if (start == null && end != null) {
            start = end;
          }

          start ??= position;
          end ??= position;
          errors.add((message: '$message', start: start, end: end));
      }
    }

    if (expected.isNotEmpty) {
      for (final position in expected.keys) {
        final list = expected[position]!.toList();
        final message = 'Expected: ${list.map((e) => '\'$e\'').join(', ')}';
        errors.add((message: message, start: position, end: position));
      }
    }

    if (errors.isEmpty) {
      errors.add((
        message: 'Syntax error',
        start: farthestPosition,
        end: farthestPosition,
      ));
    }

    return errors.toSet().toList();
  }

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Returns the position of the first match of [string] in input, starting at
  /// [position,.
  int indexOf(String string) => _input.indexOf(string, position);

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Reads the next character, advances the position to the next character and
  /// returns that character.
  int nextChar() {
    if (position < length) {
      position += charSize(ch);
      if (predicate == 0 && farthestPosition < position) {
        farthestPosition = position;
      }

      return ch = charAt(position);
    }

    return ch = -1;
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  int readChar(int position) {
    ch = charAt(position);
    this.position = position < length ? position : length;
    if (predicate == 0 && farthestPosition < position) {
      farthestPosition = position;
    }

    return ch;
  }

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  @pragma('vm:unsafe:no-interrupts')
  /// The [startsWith] method is used to check if a string begins with a
  /// specified [substring].
  bool startsWith(String substring, [int? position]) {
    if (substring.isNotEmpty) {
      position ??= this.position;
      final count = substring.length - 1;
      if (position + count < length) {
        if (_input.codeUnitAt(position) == substring.codeUnitAt(0)) {
          for (var i = 1; i <= count; i++) {
            if (_input.codeUnitAt(position + i) != substring.codeUnitAt(i)) {
              return false;
            }
          }
        }

        return true;
      }
    } else {
      return true;
    }

    return false;
  }

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Returns the lengths of the input data.
  int strlen(String string) => string.length;

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Returns the substring of the input data.
  String substring(int start, int end) => _input.substring(start, end);

  @override
  String toString() {
    if (position >= length) {
      return '';
    }

    var rest = length - position;
    if (rest > 80) {
      rest = 80;
    }

    var line = substring(position, position + rest);
    line = line.replaceAll('\n', r'\n');
    return '|$position|$line';
  }
}
0
likes
160
points
465
downloads

Publisher

unverified uploader

Weekly Downloads

All in one, a generator of recursive descent PEG parsers, tokenizers, and token stream parsers.

Repository (GitHub)
View/report issues

Topics

#parser-generator #parsing #peg

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

simple_sparse_list, source_span, strings, unicode

More

Packages that depend on smart_parser