peg 8.0.1 copy "peg: ^8.0.1" to clipboard
peg: ^8.0.1 copied to clipboard

discontinued

Command line tool for generating a PEG (with some syntactic sugar) parsers

example/example.dart

// ignore_for_file: prefer_conditional_assignment, prefer_final_locals

import 'package:source_span/source_span.dart';

void main() {
  const source = ' 1 + 2 * 3 + x ';
  final result = calc(source, {'x': 5});
  print(result);
}

int calc(String source, Map<String, int> vars) {
  final parser = CalcParser(vars);
  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;
}

class CalcParser {
  Map<String, int> vars = {};

  CalcParser(this.vars);

  /// **EOF** ('end of file')
  ///
  ///```text
  /// `void`
  /// EOF =
  ///    !.
  ///```
  (void,)? parseEOF(State state) {
    final $2 = state.failure;
    state.failure = state.position;
    final $1 = state.position;
    final $0 = state.matchEof();
    state.expected($0, 'end of file', $1, false);
    state.failure = state.failure < $2 ? $2 : state.failure;
    return $0;
  }

  /// **Expr** ('expression')
  ///
  ///```text
  /// `int`
  /// Expr =
  ///    Sum
  ///```
  (int,)? parseExpr(State state) {
    final $2 = state.failure;
    state.failure = state.position;
    final $1 = state.position;
    final $0 = parseSum(state);
    state.expected($0, 'expression', $1, false);
    state.failure = state.failure < $2 ? $2 : state.failure;
    return $0;
  }

  /// **ID**
  ///
  ///```text
  /// `String`
  /// ID =
  ///    $ = <[a-zA-Z]> S
  ///```
  (String,)? parseID(State state) {
    final $4 = state.position;
    (String,)? $0;
    (int,)? $3;
    if (state.position < state.length) {
      final c = state.nextChar16();
      final ok = c >= 97 ? c <= 122 : c >= 65 && c <= 90;
      $3 = ok ? (c,) : null;
      $3 ?? (state.position = $4);
    }
    final $2 = $3 ?? state.fail<int>();
    final $1 = $2 != null ? (state.substring($4, state.position),) : null;
    if ($1 != null) {
      String $ = $1.$1;
      parseS(state);
      $0 = ($,);
    }
    return $0;
  }

  /// **NUMBER**
  ///
  ///```text
  /// `int`
  /// NUMBER =
  ///    n = <[0-9]+> S $ = { }
  ///```
  (int,)? parseNUMBER(State state) {
    final $4 = state.position;
    (int,)? $0;
    while (state.position < state.length) {
      final position = state.position;
      final c = state.nextChar16();
      final ok = c >= 48 && c <= 57;
      if (!ok) {
        state.position = position;
        break;
      }
    }
    final $3 =
        state.position != $4 ? const (<int>[],) : state.fail<List<int>>();
    final $1 = $3 != null ? (state.substring($4, state.position),) : null;
    if ($1 != null) {
      String n = $1.$1;
      parseS(state);
      final int $$;
      $$ = int.parse(n);
      final $2 = ($$,);
      int $ = $2.$1;
      $0 = ($,);
    }
    if ($0 == null) {
      state.position = $4;
    }
    return $0;
  }

  /// **Product**
  ///
  ///```text
  /// `int`
  /// Product =
  ///    $ = Value ([*] S r = Value { } / [/] S r = Value { })*
  ///```
  (int,)? parseProduct(State state) {
    (int,)? $0;
    final $1 = parseValue(state);
    if ($1 != null) {
      int $ = $1.$1;
      while (true) {
        final $6 = state.position;
        (void,)? $2;
        (int,)? $5;
        if (state.position < state.length) {
          final c = state.nextChar16();
          $5 = c == 42 ? (42,) : null;
          $5 ?? (state.position = $6);
        }
        final $3 = $5 ?? state.fail<int>();
        if ($3 != null) {
          parseS(state);
          final $4 = parseValue(state);
          if ($4 != null) {
            int r = $4.$1;
            $ *= r;
            $2 = (null,);
          }
        }
        if ($2 == null) {
          state.position = $6;
        }
        if ($2 == null) {
          (int,)? $9;
          if (state.position < state.length) {
            final c = state.nextChar16();
            $9 = c == 47 ? (47,) : null;
            $9 ?? (state.position = $6);
          }
          final $7 = $9 ?? state.fail<int>();
          if ($7 != null) {
            parseS(state);
            final $8 = parseValue(state);
            if ($8 != null) {
              int r = $8.$1;
              $ ~/= r;
              $2 = (null,);
            }
          }
          if ($2 == null) {
            state.position = $6;
          }
        }
        if ($2 == null) {
          break;
        }
      }
      $0 = ($,);
    }
    return $0;
  }

  /// **S**
  ///
  ///```text
  /// `void`
  /// S =
  ///    [ {9}{d}{a}]*
  ///```
  (void,)? parseS(State state) {
    while (state.position < state.length) {
      final position = state.position;
      final c = state.nextChar16();
      final ok = c >= 13 ? c <= 13 || c == 32 : c >= 9 && c <= 10;
      if (!ok) {
        state.position = position;
        break;
      }
    }
    const $0 = (<int>[],);
    return $0;
  }

  /// **Start**
  ///
  ///```text
  /// `int`
  /// Start =
  ///    S $ = Expr EOF
  ///```
  (int,)? parseStart(State state) {
    final $3 = state.position;
    (int,)? $0;
    parseS(state);
    final $1 = parseExpr(state);
    if ($1 != null) {
      int $ = $1.$1;
      final $2 = parseEOF(state);
      if ($2 != null) {
        $0 = ($,);
      }
    }
    if ($0 == null) {
      state.position = $3;
    }
    return $0;
  }

  /// **Sum**
  ///
  ///```text
  /// `int`
  /// Sum =
  ///    $ = Product ([+] S r = Product { } / [\-] S r = Product { })*
  ///```
  (int,)? parseSum(State state) {
    (int,)? $0;
    final $1 = parseProduct(state);
    if ($1 != null) {
      int $ = $1.$1;
      while (true) {
        final $6 = state.position;
        (void,)? $2;
        (int,)? $5;
        if (state.position < state.length) {
          final c = state.nextChar16();
          $5 = c == 43 ? (43,) : null;
          $5 ?? (state.position = $6);
        }
        final $3 = $5 ?? state.fail<int>();
        if ($3 != null) {
          parseS(state);
          final $4 = parseProduct(state);
          if ($4 != null) {
            int r = $4.$1;
            $ += r;
            $2 = (null,);
          }
        }
        if ($2 == null) {
          state.position = $6;
        }
        if ($2 == null) {
          (int,)? $9;
          if (state.position < state.length) {
            final c = state.nextChar16();
            $9 = c == 45 ? (45,) : null;
            $9 ?? (state.position = $6);
          }
          final $7 = $9 ?? state.fail<int>();
          if ($7 != null) {
            parseS(state);
            final $8 = parseProduct(state);
            if ($8 != null) {
              int r = $8.$1;
              $ -= r;
              $2 = (null,);
            }
          }
          if ($2 == null) {
            state.position = $6;
          }
        }
        if ($2 == null) {
          break;
        }
      }
      $0 = ($,);
    }
    return $0;
  }

  /// **Value** ('expression')
  ///
  ///```text
  /// `int`
  /// Value =
  ///    (NUMBER / i = ID $ = { } / '(' S $ = Expr ')' S)
  ///```
  (int,)? parseValue(State state) {
    final $7 = state.failure;
    state.failure = state.position;
    final $1 = state.position;
    (int,)? $0;
    $0 = parseNUMBER(state);
    if ($0 == null) {
      final $2 = parseID(state);
      if ($2 != null) {
        String i = $2.$1;
        final int $$;
        $$ = vars[i]!;
        final $3 = ($$,);
        int $ = $3.$1;
        $0 = ($,);
      }
      if ($0 == null) {
        final $4 = state.matchLiteral1(('(',), '(', 40);
        if ($4 != null) {
          parseS(state);
          final $5 = parseExpr(state);
          if ($5 != null) {
            int $ = $5.$1;
            final $6 = state.matchLiteral1((')',), ')', 41);
            if ($6 != null) {
              parseS(state);
              $0 = ($,);
            }
          }
        }
        if ($0 == null) {
          state.position = $1;
        }
      }
    }
    state.expected($0, 'expression', $1, false);
    state.failure = state.failure < $7 ? $7 : state.failure;
    return $0;
  }
}

class State {
  /// The position of the parsing failure.
  int failure = 0;

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

  /// Indicates that parsing occurs within a `not' predicate`.
  ///
  /// When parsed within the `not predicate`, all `expected` errors are
  /// converted to `unexpected` errors.
  bool predicate = false;

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

  int _errorIndex = 0;

  int _expectedIndex = 0;

  final List<String?> _expected = List.filled(128, null);

  int _farthestError = 0;

  int _farthestFailure = 0;

  int _farthestFailureLength = 0;

  int _farthestUnexpected = 0;

  final String _input;

  final List<bool?> _locations = List.filled(128, null);

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

  final List<int?> _positions = List.filled(128, null);

  int _unexpectedIndex = 0;

  final List<String?> _unexpectedElements = List.filled(128, null);

  final List<int?> _unexpectedPositions = List.filled(128, null);

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

  /// This method is for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  void error(String message, {bool? location}) {
    if (_farthestError > failure) {
      return;
    }

    if (_farthestError < failure) {
      _farthestError = failure;
      _errorIndex = 0;
      _expectedIndex = 0;
    }

    if (_errorIndex < _messages.length) {
      _locations[_errorIndex] = location;
      _messages[_errorIndex] = message;
      _positions[_errorIndex] = position;
      _errorIndex++;
    }
  }

  /// This method is for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  void expected(Object? result, String element, int start,
      [bool literal = true]) {
    if (_farthestError > position) {
      return;
    }

    if (result != null) {
      if (!predicate || _farthestUnexpected > position) {
        return;
      }

      if (_farthestUnexpected < position) {
        _farthestUnexpected = position;
        _unexpectedIndex = 0;
      }

      if (_farthestError < position) {
        _farthestError = position;
        _errorIndex = 0;
        _expectedIndex = 0;
      }

      if (_unexpectedIndex < _unexpectedElements.length) {
        _unexpectedElements[_unexpectedIndex] = element;
        _unexpectedPositions[_unexpectedIndex] = start;
        _unexpectedIndex++;
      }
    } else {
      if (!literal && failure != position) {
        return;
      }

      if (_farthestError < position) {
        _farthestError = position;
        _errorIndex = 0;
        _expectedIndex = 0;
      }

      if (!literal) {
        _expectedIndex = 0;
      }

      if (_expectedIndex < _expected.length) {
        _expected[_expectedIndex++] = element;
      }
    }
  }

  /// Causes a parsing failure and updates the [failure] and [_farthestFailure]
  /// positions.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (T,)? fail<T>([int length = 0]) {
    failure < position ? failure = position : null;
    if (_farthestFailure > position) {
      return null;
    }

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

    _farthestFailureLength =
        _farthestFailureLength < length ? length : _farthestFailureLength;
    return null;
  }

  /// This method is for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (T,)? failAndBacktrack<T>(int position) {
    fail<void>(this.position - position);
    this.position = position;
    return null;
  }

  /// 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})>[];
    for (var i = 0; i < _errorIndex; i++) {
      final message = _messages[i];
      if (message == null) {
        continue;
      }

      var start = _positions[i]!;
      var end = _farthestError;
      final location = _locations[i];
      switch (location) {
        case true:
          start = end;
          break;
        case false:
          end = start;
          break;
        default:
      }

      errors.add((message: message, start: start, end: end));
    }

    if (_expectedIndex > 0) {
      final names = <String>[];
      for (var i = 0; i < _expectedIndex; i++) {
        final name = _expected[i]!;
        names.add(name);
      }

      names.sort();
      final message =
          'Expected: ${names.toSet().map((e) => '\'$e\'').join(', ')}';
      errors
          .add((message: message, start: _farthestError, end: _farthestError));
    }

    if (_farthestUnexpected >= _farthestError) {
      if (_unexpectedIndex > 0) {
        for (var i = 0; i < _unexpectedIndex; i++) {
          final element = _unexpectedElements[i]!;
          final position = _unexpectedPositions[i]!;
          final message = "Unexpected '$element'";
          errors.add(
              (message: message, start: position, end: _farthestUnexpected));
        }
      }
    }

    if (errors.isEmpty) {
      errors.add((
        message: 'Unexpected input data',
        start: _farthestFailure - _farthestFailureLength,
        end: _farthestFailure
      ));
    }

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

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? match<R>((R,) result, String string) {
    final start = position;
    if (position + string.length <= length) {
      for (var i = 0; i < string.length; i++) {
        if (string.codeUnitAt(i) != nextChar16()) {
          position = start;
          return fail();
        }
      }
    }

    return result;
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? match1<R>((R,) result, int c) {
    final start = position;
    if (position < length && c == nextChar16()) {
      return result;
    }

    position = start;
    return fail();
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? match2<R>((R,) result, int c1, int c2) {
    final start = position;
    if (position + 1 < length && c1 == nextChar16() && c2 == nextChar16()) {
      return result;
    }

    position = start;
    return fail();
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? match3<R>((R,) result, int c1, int c2, int c3) {
    final start = position;
    if (position + 2 < length &&
        c1 == nextChar16() &&
        c2 == nextChar16() &&
        c3 == nextChar16()) {
      return result;
    }

    position = start;
    return fail();
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? match4<R>((R,) result, int c1, int c2, int c3, int c4) {
    final start = position;
    if (position + 3 < length &&
        c1 == nextChar16() &&
        c2 == nextChar16() &&
        c3 == nextChar16() &&
        c4 == nextChar16()) {
      return result;
    }

    position = start;
    return fail();
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? match5<R>((R,) result, int c1, int c2, int c3, int c4, int c5) {
    final start = position;
    if (position + 4 < length &&
        c1 == nextChar16() &&
        c2 == nextChar16() &&
        c3 == nextChar16() &&
        c4 == nextChar16() &&
        c5 == nextChar16()) {
      return result;
    }

    position = start;
    return fail();
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (int,)? matchAny() {
    if (position < length) {
      return (nextChar32(),);
    }

    return fail();
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (void,)? matchEof() {
    return position >= length ? (null,) : fail();
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? matchLiteral<R>((R,) result, String literal) {
    final start = position;
    final actual = match(result, literal);
    expected(actual, literal, start, true);
    return actual;
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? matchLiteral1<R>((R,) result, String string, int c) {
    final start = position;
    final actual = match1(result, c);
    expected(actual, string, start, true);
    return actual;
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? matchLiteral2<R>((R,) result, String string, int c1, int c2) {
    final start = position;
    final actual = match2(result, c1, c2);
    expected(actual, string, start, true);
    return actual;
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? matchLiteral3<R>((R,) result, String string, int c1, int c2, int c3) {
    final start = position;
    final actual = match3(result, c1, c2, c3);
    expected(actual, string, start, true);
    return actual;
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? matchLiteral4<R>(
      (R,) result, String string, int c1, int c2, int c3, int c4) {
    final start = position;
    final actual = match4(result, c1, c2, c3, c4);
    expected(actual, string, start, true);
    return actual;
  }

  /// Intended for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  (R,)? matchLiteral5<R>(
      (R,) result, String string, int c1, int c2, int c3, int c4, int c5) {
    final start = position;
    final actual = match5(result, c1, c2, c3, c4, c5);
    expected(actual, string, start, true);
    return actual;
  }

  /// This method is for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  int nextChar16() => _input.codeUnitAt(position++);

  /// This method is for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  int nextChar32() {
    final c = _input.readChar(position);
    position += c > 0xffff ? 2 : 1;
    return c;
  }

  /// This method is for internal use only.
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  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;
    }

    // Need to create the equivalent of 'substring'
    var line = substring(position, position + rest);
    line = line.replaceAll('\n', '\n');
    return '|$position|$line';
  }
}

extension on String {
  @pragma('vm:prefer-inline')
  @pragma('dart2js:tryInline')
  // ignore: unused_element
  int readChar(int index) {
    final b1 = codeUnitAt(index++);
    if (b1 > 0xd7ff && b1 < 0xe000) {
      if (index < length) {
        final b2 = codeUnitAt(index);
        if ((b2 & 0xfc00) == 0xdc00) {
          return 0x10000 + ((b1 & 0x3ff) << 10) + (b2 & 0x3ff);
        }
      }

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

    return b1;
  }
}
3
likes
0
points
286
downloads

Publisher

unverified uploader

Weekly Downloads

Command line tool for generating a PEG (with some syntactic sugar) parsers

Repository (GitHub)
View/report issues

Topics

#parser #parser-generator #peg #top-down-parser

License

unknown (license)

Dependencies

args, path, simple_sparse_list, source_span, strings

More

Packages that depend on peg