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

This software is a library that generates source code of recursive descent parsers based on a grammar consisting of the parsing expressions and native Dart language source code

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);
    throw FormatException(
      state
          .getErrors()
          .map((e) => file.span(e.start, e.end).message(e.message))
          .join('\n'),
    );
  }

  return result.$1;
}

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

  /// [Object?] **Start**
  /// ```txt
  /// `Object?` Start =>
  ///   S
  ///   $ = Value
  ///   & { state.peek() < 0 }
  /// ```
  Result<Object?>? parseStart(State state) {
    final $0 = state.position;
    parseS(state);
    final $1 = parseValue(state);
    if ($1 != null) {
      final $2 = state.peek() < 0;
      if ($2) {
        return $1;
      } else {
        state.backtrack($0);
      }
    } else {
      state.backtrack($0);
    }
    return null;
  }

  /// [List<Object?>] **Elements**
  /// ```txt
  /// `List<Object?>` Elements =>
  ///   v = Value
  ///   { final l = [v]; }
  ///   @while (0) {
  ///     ',' S
  ///     v = Value
  ///     { l.add(v); }
  ///   }
  ///   $ = { l }
  /// ```
  Result<List<Object?>>? parseElements(State state) {
    final $0 = parseValue(state);
    if ($0 != null) {
      final v = $0.$1;
      final l = [v];
      // (0)
      while (true) {
        final $1 = state.position;
        final $2 = state.peek();
        // ','
        if ($2 == 44) {
          state.position += 1;
          parseS(state);
          final $3 = parseValue(state);
          if ($3 != null) {
            final v = $3.$1;
            l.add(v);
            continue;
          } else {
            state.backtrack($1);
          }
        } else {
          state.errorExpected(',');
        }
        break;
      }
      final $4 = l;
      return Ok($4);
    }
    return null;
  }

  /// [List<Object?>] **Array**
  /// ```txt
  /// `List<Object?>` Array =>
  ///   '[' S
  ///   e = Elements?
  ///   ']' S
  ///   $ = { e ?? [] }
  /// ```
  Result<List<Object?>>? parseArray(State state) {
    final $0 = state.position;
    final $1 = state.peek();
    // '['
    if ($1 == 91) {
      state.position += 1;
      parseS(state);
      final $2 = parseElements(state);
      final e = $2?.$1;
      final $3 = state.peek();
      // ']'
      if ($3 == 93) {
        state.position += 1;
        parseS(state);
        final $4 = e ?? [];
        return Ok($4);
      } else {
        state.errorExpected(']');
        state.backtrack($0);
      }
    } else {
      state.errorExpected('[');
    }
    return null;
  }

  /// [MapEntry<String, Object?>] **KeyValue**
  /// ```txt
  /// `MapEntry<String, Object?>` KeyValue =>
  ///   k = String
  ///   ':' S
  ///   v = Value
  ///   $ = { MapEntry(k, v) }
  /// ```
  Result<MapEntry<String, Object?>>? parseKeyValue(State state) {
    final $0 = state.position;
    final $1 = parseString(state);
    if ($1 != null) {
      final k = $1.$1;
      final $2 = state.peek();
      // ':'
      if ($2 == 58) {
        state.position += 1;
        parseS(state);
        final $3 = parseValue(state);
        if ($3 != null) {
          final v = $3.$1;
          final $4 = MapEntry(k, v);
          return Ok($4);
        } else {
          state.backtrack($0);
        }
      } else {
        state.errorExpected(':');
        state.backtrack($0);
      }
    }
    return null;
  }

  /// [Map<String, Object?>] **Map**
  /// ```txt
  /// `Map<String, Object?>` Map =>
  ///   v = KeyValue
  ///   {
  ///     final m = <String, Object?>{};
  ///     m[v.key] = v.value;
  ///   }
  ///   @while (0) {
  ///     ',' S
  ///     v = KeyValue
  ///     { m[v.key] = v.value; }
  ///   }
  ///   $ = { m }
  /// ```
  Result<Map<String, Object?>>? parseMap(State state) {
    final $0 = parseKeyValue(state);
    if ($0 != null) {
      final v = $0.$1;
      final m = <String, Object?>{};
      m[v.key] = v.value;
      // (0)
      while (true) {
        final $1 = state.position;
        final $2 = state.peek();
        // ','
        if ($2 == 44) {
          state.position += 1;
          parseS(state);
          final $3 = parseKeyValue(state);
          if ($3 != null) {
            final v = $3.$1;
            m[v.key] = v.value;
            continue;
          } else {
            state.backtrack($1);
          }
        } else {
          state.errorExpected(',');
        }
        break;
      }
      final $4 = m;
      return Ok($4);
    }
    return null;
  }

  /// [Map<String, Object?>] **Object**
  /// ```txt
  /// `Map<String, Object?>` Object =>
  ///   '{' S
  ///   m = Map?
  ///   '}' S
  ///   $ = { m ?? {} }
  /// ```
  Result<Map<String, Object?>>? parseObject(State state) {
    final $0 = state.position;
    final $1 = state.peek();
    // '{'
    if ($1 == 123) {
      state.position += 1;
      parseS(state);
      final $2 = parseMap(state);
      final m = $2?.$1;
      final $3 = state.peek();
      // '}'
      if ($3 == 125) {
        state.position += 1;
        parseS(state);
        final $4 = m ?? {};
        return Ok($4);
      } else {
        state.errorExpected('}');
        state.backtrack($0);
      }
    } else {
      state.errorExpected('{');
    }
    return null;
  }

  /// [String] **Hex**
  /// ```txt
  /// `String` Hex =>
  ///   s = <
  ///     @while (4, 4) {
  ///       [a-fA-F0-9]
  ///     }
  ///   >
  ///   $ = { String.fromCharCode(int.parse(s, radix: 16)) }
  ///   ~ {
  ///     state.errorIncorrect('Expected hexadecimal digit', false);
  ///     state.errorIncorrect('Unterminated 4 hexadecimal digit number', true);
  ///     state.errorExpected('4 hexadecimal digit number');
  ///   }
  /// ```
  Result<String>? parseHex(State state) {
    final $0 = state.beginErrorHandling();
    final $1 = state.position;
    var $2 = 0;
    // (4, 4)
    while ($2 < 4) {
      final $3 = state.peek();
      // [a-fA-F0-9]
      final $4 = $3 <= 70 ? $3 >= 65 || $3 >= 48 && $3 <= 57 : $3 >= 97 && $3 <= 102;
      if ($4) {
        state.position += 1;
        $2++;
        continue;
      }
      break;
    }
    if ($2 >= 4) {
      final $5 = state.substring($1, state.position);
      final s = $5;
      final $6 = String.fromCharCode(int.parse(s, radix: 16));
      state.endErrorHandling($0);
      return Ok($6);
    } else {
      state.backtrack($1);
      state.errorIncorrect('Expected hexadecimal digit', false);
      state.errorIncorrect('Unterminated 4 hexadecimal digit number', true);
      state.errorExpected('4 hexadecimal digit number');
      state.endErrorHandling($0);
    }
    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) {
    Result<String>? $0;
    final $1 = state.beginErrorHandling();
    $l:
    {
      final $2 = state.peek();
      // ["]
      if ($2 == 34) {
        state.position += 1;
        const $3 = '"';
        $0 = const Ok($3);
        break $l;
      }
      // [\\]
      if ($2 == 92) {
        state.position += 1;
        const $4 = '\\';
        $0 = const Ok($4);
        break $l;
      }
      // [/]
      if ($2 == 47) {
        state.position += 1;
        const $5 = '/';
        $0 = const Ok($5);
        break $l;
      }
      // [b]
      if ($2 == 98) {
        state.position += 1;
        const $6 = '\b';
        $0 = const Ok($6);
        break $l;
      }
      // [f]
      if ($2 == 102) {
        state.position += 1;
        const $7 = '\f';
        $0 = const Ok($7);
        break $l;
      }
      // [n]
      if ($2 == 110) {
        state.position += 1;
        const $8 = '\n';
        $0 = const Ok($8);
        break $l;
      }
      // [r]
      if ($2 == 114) {
        state.position += 1;
        const $9 = '\r';
        $0 = const Ok($9);
        break $l;
      }
      // [t]
      if ($2 == 116) {
        state.position += 1;
        const $10 = '\t';
        $0 = const Ok($10);
        break $l;
      }
    }
    if ($0 != null) {
      state.endErrorHandling($1);
      return $0;
    } else {
      if (state.position == state.length) {
        state.errorExpected('escape character');
      } else {
        state.error('Illegal escape character');
      }
      state.endErrorHandling($1);
    }
    return null;
  }

  /// [String] **Escaped**
  /// ```txt
  /// `String` Escaped =>
  ///   [u]
  ///   $ = Hex
  ///   ----
  ///   EscapeC
  /// ```
  Result<String>? parseEscaped(State state) {
    final $0 = state.position;
    final $1 = state.peek();
    // [u]
    if ($1 == 117) {
      state.position += 1;
      final $2 = parseHex(state);
      if ($2 != null) {
        return $2;
      } else {
        state.backtrack($0);
      }
    }
    final $3 = parseEscapeC(state);
    if ($3 != null) {
      return $3;
    }
    return null;
  }

  /// [String] **String**
  /// ```txt
  /// `String` String =>
  ///   '"' S
  ///   p = @while (0) {
  ///     <[^{0-1F}"\\]+>
  ///     ---
  ///     [\\]
  ///     $ = Escaped
  ///   }
  ///   '"' S
  ///   $ = { p.join() }
  /// ```
  Result<String>? parseString(State state) {
    final $0 = state.position;
    final $1 = state.peek();
    // '"'
    if ($1 == 34) {
      state.position += 1;
      parseS(state);
      final $2 = <String>[];
      // (0)
      while (true) {
        final $3 = state.position;
        var $4 = false;
        // (1)
        while (true) {
          final $5 = state.peek();
          // [^{0-1f}"\\]
          final $6 = !($5 <= 34 ? $5 >= 34 || $5 >= 0 && $5 <= 31 : $5 == 92) && !($5 < 0);
          if ($6) {
            state.position += $5 > 0xffff ? 2 : 1;
            $4 = true;
            continue;
          }
          break;
        }
        if ($4) {
          final $7 = state.substring($3, state.position);
          $2.add($7);
          continue;
        }
        final $8 = state.peek();
        // [\\]
        if ($8 == 92) {
          state.position += 1;
          final $9 = parseEscaped(state);
          if ($9 != null) {
            $2.add($9.$1);
            continue;
          } else {
            state.backtrack($3);
          }
        }
        break;
      }
      final p = $2;
      final $10 = state.peek();
      // '"'
      if ($10 == 34) {
        state.position += 1;
        parseS(state);
        final $11 = p.join();
        return Ok($11);
      } else {
        state.errorExpected('"');
        state.backtrack($0);
      }
    } else {
      state.errorExpected('"');
    }
    return null;
  }

  /// [num] **Number**
  /// ```txt
  /// `num` Number =>
  ///   {
  ///     final start = state.position;
  ///     var flag = true;
  ///   }
  ///   [-]? ([0] / [1-9] [0-9]*)
  ///   (
  ///     ([.] [0-9]+)
  ///     { flag = false; }
  ///     ~ { state.errorIncorrect('Fractional part is missing a number'); }
  ///   )?
  ///   (
  ///     ([eE] [\-+]? [0-9]+)
  ///     { flag = false; }
  ///     ~ { state.errorIncorrect('Exponent part is missing a number'); }
  ///   )?
  ///   s = { state.substring(start, state.position) }
  ///   S
  ///   $ = { flag && s.length <= 18 ? int.parse(s) : num.parse(s) }
  ///   ~ {
  ///     state.errorIncorrect('Unterminated number', true);
  ///     state.errorExpected('number');
  ///   }
  /// ```
  Result<num>? parseNumber(State state) {
    Result<num>? $0;
    final $1 = state.beginErrorHandling();
    $l:
    {
      final $2 = state.position;
      final start = state.position;
      var flag = true;
      final $3 = state.peek();
      // [\-]
      if ($3 == 45) {
        state.position += 1;
      }
      var $4 = false;
      $l1:
      {
        final $5 = state.peek();
        // [0]
        if ($5 == 48) {
          state.position += 1;
          $4 = true;
          break $l1;
        }
        // [1-9]
        final $6 = $5 >= 49 && $5 <= 57;
        if ($6) {
          state.position += 1;
          // (0)
          while (true) {
            final $7 = state.peek();
            // [0-9]
            final $8 = $7 >= 48 && $7 <= 57;
            if ($8) {
              state.position += 1;
              continue;
            }
            break;
          }
          $4 = true;
          break $l1;
        }
      }
      if ($4) {
        var $9 = false;
        final $10 = state.beginErrorHandling();
        $l2:
        {
          final $11 = state.position;
          final $12 = state.peek();
          // [.]
          if ($12 == 46) {
            state.position += 1;
            var $13 = false;
            // (1)
            while (true) {
              final $14 = state.peek();
              // [0-9]
              final $15 = $14 >= 48 && $14 <= 57;
              if ($15) {
                state.position += 1;
                $13 = true;
                continue;
              }
              break;
            }
            if ($13) {
              flag = false;
              $9 = true;
              break $l2;
            } else {
              state.backtrack($11);
            }
          }
        }
        if ($9) {
          state.endErrorHandling($10);
        } else {
          state.errorIncorrect('Fractional part is missing a number');
          state.endErrorHandling($10);
        }
        var $16 = false;
        final $17 = state.beginErrorHandling();
        $l3:
        {
          final $18 = state.position;
          final $19 = state.peek();
          // [eE]
          final $20 = $19 == 69 || $19 == 101;
          if ($20) {
            state.position += 1;
            final $21 = state.peek();
            // [\-+]
            final $22 = $21 == 43 || $21 == 45;
            if ($22) {
              state.position += 1;
            }
            var $23 = false;
            // (1)
            while (true) {
              final $24 = state.peek();
              // [0-9]
              final $25 = $24 >= 48 && $24 <= 57;
              if ($25) {
                state.position += 1;
                $23 = true;
                continue;
              }
              break;
            }
            if ($23) {
              flag = false;
              $16 = true;
              break $l3;
            } else {
              state.backtrack($18);
            }
          }
        }
        if ($16) {
          state.endErrorHandling($17);
        } else {
          state.errorIncorrect('Exponent part is missing a number');
          state.endErrorHandling($17);
        }
        final $26 = state.substring(start, state.position);
        final s = $26;
        parseS(state);
        final $27 = flag && s.length <= 18 ? int.parse(s) : num.parse(s);
        $0 = Ok($27);
        break $l;
      } else {
        state.backtrack($2);
      }
    }
    if ($0 != null) {
      state.endErrorHandling($1);
      return $0;
    } else {
      state.errorIncorrect('Unterminated number', true);
      state.errorExpected('number');
      state.endErrorHandling($1);
    }
    return null;
  }

  /// [Object?] **Value**
  /// ```txt
  /// `Object?` Value =>
  ///   'null' S
  ///   $ = `const` { null }
  ///   ----
  ///   'true' S
  ///   $ = `const` { true }
  ///   ----
  ///   'false' S
  ///   $ = `const` { false }
  ///   ----
  ///   Object
  ///   ----
  ///   Array
  ///   ----
  ///   String
  ///   ----
  ///   Number
  /// ```
  Result<Object?>? parseValue(State state) {
    final $0 = state.peek();
    if ($0 == 110 && state.startsWith('null')) {
      state.position += 4;
      parseS(state);
      const $1 = null;
      return const Ok($1);
    } else {
      state.errorExpected('null');
    }
    if ($0 == 116 && state.startsWith('true')) {
      state.position += 4;
      parseS(state);
      const $2 = true;
      return const Ok($2);
    } else {
      state.errorExpected('true');
    }
    if ($0 == 102 && state.startsWith('false')) {
      state.position += 5;
      parseS(state);
      const $3 = false;
      return const Ok($3);
    } else {
      state.errorExpected('false');
    }
    final $4 = parseObject(state);
    if ($4 != null) {
      return $4;
    }
    final $5 = parseArray(state);
    if ($5 != null) {
      return $5;
    }
    final $6 = parseString(state);
    if ($6 != null) {
      return $6;
    }
    final $7 = parseNumber(state);
    if ($7 != null) {
      return $7;
    }
    return null;
  }

  /// [void] **S**
  /// ```txt
  /// `void` S =>
  ///     [\n\r\t ]*
  /// ```
  Result<void> parseS(State state) {
    // (0)
    while (true) {
      final $0 = state.peek();
      // [\n\r\t ]
      final $1 = $0 <= 13 ? $0 >= 13 || $0 >= 9 && $0 <= 10 : $0 == 32;
      if ($1) {
        state.position += 1;
        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 _expected = Object();

  static const _maxErrorCount = 64;

  /// The farthest local parsing position.
  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 _ch = 0;

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

  int _errorCount = 0;

  int _errorState = -1;

  int _farthestError = 0;

  int _farthestPosition = 0;

  final String _input;

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

  int _peekPosition = -1;

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

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

  void backtrack(int position) {
    if (position > this.position) {
      throw RangeError.range(position, 0, this.position, 'position');
    }

    if (predicate == 0) {
      if (_farthestPosition < this.position) {
        _farthestPosition = this.position;
      }

      if (farthestPosition < this.position) {
        farthestPosition = this.position;
      }
    }

    this.position = position;
  }

  /// Intended for internal use only.
  /// @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  int beginErrorHandling() {
    final farthestPosition = this.farthestPosition;
    this.farthestPosition = position;
    return farthestPosition;
  }

  @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;

  /// Intended for internal use only.
  /// @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  void endErrorHandling(int farthestPosition) {
    if (this.farthestPosition < farthestPosition) {
      this.farthestPosition = farthestPosition;
    }
  }

  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Adds (if possible) an error to the error buffer.
  ///
  /// Parameters:
  ///
  ///  - [message]: The message text used when building error messages.
  ///  - [fullSpan]: Specifies information about the location of the error.
  ///  - `true` - (start, end)
  ///  - `false` - (end, end)
  ///  - `null` - (start, start)
  void error(String message, [bool? fullSpan = false]) {
    if (predicate != 0) {
      return;
    }

    if (_farthestError <= farthestPosition) {
      if (_farthestError < farthestPosition) {
        _farthestError = farthestPosition;
        _errorCount = 0;
      }

      if (_errorCount < _messages.length) {
        _flags[_errorCount] = fullSpan;
        _messages[_errorCount] = message;
        _starts[_errorCount] = position;
        _errorCount++;
      }
    }
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Adds (if possible) an `expected` error to the error buffer at the current
  /// position.
  ///
  /// Parameters:
  ///
  ///  - [element]: A specific syntactic element that was expected but was
  /// missing.
  void errorExpected(String element) {
    if (predicate != 0) {
      return;
    }

    if (_farthestError <= position) {
      if (_farthestError < position) {
        _farthestError = position;
        _errorCount = 0;
      }

      if (_errorCount < _messages.length) {
        _flags[_errorCount] = _expected;
        _messages[_errorCount] = element;
        _errorCount++;
      }
    }

    if (_farthestPosition < position) {
      _farthestPosition = position;
    }

    if (farthestPosition < position) {
      farthestPosition = position;
    }
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  /// Adds (if possible) an error to the error buffer in the case where the
  /// error has a position further than the staring parsing position.
  ///
  /// Parameters:
  ///
  ///  - [message]: The message text used when building error messages.
  ///  - [fullSpan]: Specifies information about the location of the error.
  ///  - `true` - (start, end)
  ///  - `false` - (end, end)
  ///  - `null` - (start, start)
  void errorIncorrect(String message, [bool? fullSpan = false]) {
    if (predicate != 0) {
      return;
    }

    if (farthestPosition > position) {
      error(message, fullSpan);
    }
  }

  /// Converts error messages to errors and returns them as an error list.
  List<({int end, String message, int start})> getErrors() {
    final errors = <({int end, String message, int start})>[];
    if (_farthestPosition < position) {
      _farthestPosition = position;
    }

    final end = _farthestError;
    final expected = <String>{};
    for (var i = 0; i < _errorCount; i++) {
      final message = _messages[i];
      if (message == null) {
        continue;
      }

      switch (_flags[i]) {
        case _expected:
          expected.add(message);
          break;
        case true:
          final start = _starts[i]!;
          errors.add((message: message, start: start, end: end));
          break;
        case false:
          errors.add((message: message, start: end, end: end));
          break;
        case null:
          final start = _starts[i]!;
          errors.add((message: message, start: start, end: start));
          break;
        default:
          errors.add((message: message, start: end, end: end));
      }
    }

    if (expected.isNotEmpty) {
      final list = expected.toList();
      list.sort();
      final message = 'Expected: ${list.map((e) => '\'$e\'').join(', ')}';
      errors.add((
        message: message,
        start: _farthestError,
        end: _farthestError,
      ));
    }

    if (errors.isEmpty) {
      errors.add((
        message: 'Unexpected input data',
        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);

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  int peek() {
    if (_peekPosition == position) {
      return _ch;
    }

    _peekPosition = position;
    if (position < length) {
      if ((_ch = _input.codeUnitAt(position)) < 0xd800) {
        return _ch;
      }

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

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

      return _ch;
    } else {
      return _ch = -1;
    }
  }

  /// Removes recent errors. Recent errors are errors that were added while
  /// parsing the expression.
  void removeRecentErrors() {
    if (_farthestError == position) {
      if (_errorState >= 0) {
        _errorCount = _errorState > _maxErrorCount
            ? _maxErrorCount
            : _errorState;
      }
    }
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  void restoreErrorState(int errorState) {
    _errorState = errorState;
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  int setErrorState() {
    final errorState = _errorState;
    if (_farthestError < position) {
      _errorState = 0;
    } else if (_farthestError == position) {
      _errorState = _errorCount;
    } else {
      _errorState = -1;
    }
    return errorState;
  }

  @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
199
downloads

Publisher

unverified uploader

Weekly Downloads

This software is a library that generates source code of recursive descent parsers based on a grammar consisting of the parsing expressions and native Dart language source code

Repository (GitHub)
View/report issues

Topics

#parser-generator #parsing #peg

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

simple_sparse_list, source_span, unicode

More

Packages that depend on smart_parser