--- /dev/null
+{
+ "git": {
+ "sha1": "4a99950b26cc9cb7fd483ef0983c20e66f68eb2f"
+ },
+ "path_in_vcs": "peg-runtime"
+}
\ No newline at end of file
--- /dev/null
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "peg-runtime"
+version = "0.8.1"
+authors = ["Kevin Mehall <km@kevinmehall.net>"]
+description = "Runtime support for rust-peg grammars. To use rust-peg, see the `peg` crate."
+license = "MIT"
+repository = "https://github.com/kevinmehall/rust-peg"
+
+[lib]
+path = "lib.rs"
--- /dev/null
+[package]
+name = "peg-runtime"
+version = "0.8.1"
+authors = [ "Kevin Mehall <km@kevinmehall.net>" ]
+license = "MIT"
+repository = "https://github.com/kevinmehall/rust-peg"
+description = "Runtime support for rust-peg grammars. To use rust-peg, see the `peg` crate."
+edition = "2018"
+
+[lib]
+path = "lib.rs"
\ No newline at end of file
--- /dev/null
+Copyright (C) 2013 Kevin Mehall
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
--- /dev/null
+//! Parse error reporting
+
+use crate::{Parse, RuleResult};
+use std::collections::HashSet;
+use std::fmt::{self, Debug, Display};
+
+/// A set of literals or names that failed to match
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ExpectedSet {
+ expected: HashSet<&'static str>,
+}
+
+impl ExpectedSet {
+ /// Iterator of expected literals
+ pub fn tokens<'a>(&'a self) -> impl Iterator<Item = &'static str> + 'a {
+ self.expected.iter().map(|x| *x)
+ }
+}
+
+impl Display for ExpectedSet {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ if self.expected.is_empty() {
+ write!(fmt, "<unreported>")?;
+ } else if self.expected.len() == 1 {
+ write!(fmt, "{}", self.expected.iter().next().unwrap())?;
+ } else {
+ let mut errors = self.tokens().collect::<Vec<_>>();
+ errors.sort();
+ let mut iter = errors.into_iter();
+
+ write!(fmt, "one of {}", iter.next().unwrap())?;
+ for elem in iter {
+ write!(fmt, ", {}", elem)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+/// A parse failure.
+#[derive(PartialEq, Eq, Debug, Clone)]
+pub struct ParseError<L> {
+ /// The furthest position the parser reached in the input before failing.
+ pub location: L,
+
+ /// The set of literals that failed to match at that position.
+ pub expected: ExpectedSet,
+}
+
+impl<L: Display> Display for ParseError<L> {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
+ write!(
+ fmt,
+ "error at {}: expected {}",
+ self.location, self.expected
+ )
+ }
+}
+
+impl<L: Display + Debug> ::std::error::Error for ParseError<L> {
+ fn description(&self) -> &str {
+ "parse error"
+ }
+}
+
+#[doc(hidden)]
+pub struct ErrorState {
+ /// Furthest failure we've hit so far.
+ pub max_err_pos: usize,
+
+ /// Are we inside a lookahead/quiet block? If so, failure is disabled.
+ /// Non-zero => yes, to support nested blocks.
+ pub suppress_fail: usize,
+
+ /// Are we reparsing after a failure? If so, compute and store expected set of all alternative expectations
+ /// when we are at offset `max_err_pos`.
+ pub reparsing_on_error: bool,
+
+ /// The set of tokens we expected to find when we hit the failure. Updated when `reparsing_on_error`.
+ pub expected: ExpectedSet,
+}
+
+impl ErrorState {
+ pub fn new(initial_pos: usize) -> Self {
+ ErrorState {
+ max_err_pos: initial_pos,
+ suppress_fail: 0,
+ reparsing_on_error: false,
+ expected: ExpectedSet {
+ expected: HashSet::new(),
+ },
+ }
+ }
+
+ /// Set up for reparsing to record the details of the furthest failure.
+ pub fn reparse_for_error(&mut self) {
+ self.suppress_fail = 0;
+ self.reparsing_on_error = true;
+ }
+
+ #[inline(never)]
+ pub fn mark_failure_slow_path(&mut self, pos: usize, expected: &'static str) {
+ if pos == self.max_err_pos {
+ self.expected.expected.insert(expected);
+ }
+ }
+
+ /// Flag a failure.
+ #[inline(always)]
+ pub fn mark_failure(&mut self, pos: usize, expected: &'static str) -> RuleResult<()> {
+ if self.suppress_fail == 0 {
+ if self.reparsing_on_error {
+ self.mark_failure_slow_path(pos, expected);
+ } else if pos > self.max_err_pos {
+ self.max_err_pos = pos;
+ }
+ }
+ RuleResult::Failed
+ }
+
+ pub fn into_parse_error<I: Parse + ?Sized>(self, input: &I) -> ParseError<I::PositionRepr> {
+ ParseError {
+ location: Parse::position_repr(input, self.max_err_pos.into()),
+ expected: self.expected,
+ }
+ }
+}
--- /dev/null
+use std::fmt::Display;
+
+pub mod error;
+mod slice;
+pub mod str;
+
+/// The result type used internally in the parser.
+///
+/// You'll only need this if implementing the `Parse*` traits for a custom input
+/// type. The public API of a parser adapts errors to `std::result::Result`.
+#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
+pub enum RuleResult<T> {
+ /// Success, with final location
+ Matched(usize, T),
+
+ /// Failure (furthest failure location is not yet known)
+ Failed,
+}
+
+/// A type that can be used as input to a parser.
+#[allow(clippy::needless_lifetimes)]
+pub trait Parse {
+ type PositionRepr: Display;
+ fn start<'input>(&'input self) -> usize;
+ fn is_eof<'input>(&'input self, p: usize) -> bool;
+ fn position_repr<'input>(&'input self, p: usize) -> Self::PositionRepr;
+}
+
+/// A parser input type supporting the `[...]` syntax.
+pub trait ParseElem<'input>: Parse {
+ /// Type of a single atomic element of the input, for example a character or token
+ type Element: Copy;
+
+ /// Get the element at `pos`, or `Failed` if past end of input.
+ fn parse_elem(&'input self, pos: usize) -> RuleResult<Self::Element>;
+}
+
+/// A parser input type supporting the `"literal"` syntax.
+pub trait ParseLiteral: Parse {
+ /// Attempt to match the `literal` string at `pos`, returning whether it
+ /// matched or failed.
+ fn parse_string_literal(&self, pos: usize, literal: &str) -> RuleResult<()>;
+}
+
+/// A parser input type supporting the `$()` syntax.
+pub trait ParseSlice<'input>: Parse {
+ /// Type of a slice of the input.
+ type Slice;
+
+ /// Get a slice of input.
+ fn parse_slice(&'input self, p1: usize, p2: usize) -> Self::Slice;
+}
--- /dev/null
+use super::{Parse, ParseElem, ParseLiteral, ParseSlice, RuleResult};
+
+impl<T> Parse for [T] {
+ type PositionRepr = usize;
+ fn start(&self) -> usize {
+ 0
+ }
+
+ fn is_eof(&self, pos: usize) -> bool {
+ pos >= self.len()
+ }
+
+ fn position_repr(&self, pos: usize) -> usize {
+ pos
+ }
+}
+
+impl<'input, T: 'input + Copy> ParseElem<'input> for [T] {
+ type Element = T;
+
+ fn parse_elem(&'input self, pos: usize) -> RuleResult<T> {
+ match self[pos..].first() {
+ Some(c) => RuleResult::Matched(pos + 1, *c),
+ None => RuleResult::Failed,
+ }
+ }
+}
+
+impl ParseLiteral for [u8] {
+ fn parse_string_literal(&self, pos: usize, literal: &str) -> RuleResult<()> {
+ let l = literal.len();
+ if self.len() >= pos + l && &self[pos..pos + l] == literal.as_bytes() {
+ RuleResult::Matched(pos + l, ())
+ } else {
+ RuleResult::Failed
+ }
+ }
+}
+
+impl<'input, T: 'input> ParseSlice<'input> for [T] {
+ type Slice = &'input [T];
+ fn parse_slice(&'input self, p1: usize, p2: usize) -> &'input [T] {
+ &self[p1..p2]
+ }
+}
--- /dev/null
+//! Utilities for `str` input
+
+use super::{Parse, ParseElem, ParseLiteral, ParseSlice, RuleResult};
+use std::fmt::Display;
+
+/// Line and column within a string
+#[derive(PartialEq, Eq, Debug, Clone, Copy)]
+pub struct LineCol {
+ /// Line (1-indexed)
+ pub line: usize,
+
+ /// Column (1-indexed)
+ pub column: usize,
+
+ /// Byte offset from start of string (0-indexed)
+ pub offset: usize,
+}
+
+impl Display for LineCol {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
+ write!(fmt, "{}:{}", self.line, self.column)
+ }
+}
+
+impl Parse for str {
+ type PositionRepr = LineCol;
+ fn start(&self) -> usize {
+ 0
+ }
+
+ fn is_eof(&self, pos: usize) -> bool {
+ pos >= self.len()
+ }
+
+ fn position_repr(&self, pos: usize) -> LineCol {
+ let before = &self[..pos];
+ let line = before.as_bytes().iter().filter(|&&c| c == b'\n').count() + 1;
+ let column = before.chars().rev().take_while(|&c| c != '\n').count() + 1;
+ LineCol {
+ line,
+ column,
+ offset: pos,
+ }
+ }
+}
+
+impl<'input> ParseElem<'input> for str {
+ type Element = char;
+
+ fn parse_elem(&'input self, pos: usize) -> RuleResult<char> {
+ match self[pos..].chars().next() {
+ Some(c) => RuleResult::Matched(pos + c.len_utf8(), c),
+ None => RuleResult::Failed,
+ }
+ }
+}
+
+impl ParseLiteral for str {
+ fn parse_string_literal(&self, pos: usize, literal: &str) -> RuleResult<()> {
+ let l = literal.len();
+ if self.len() >= pos + l && &self.as_bytes()[pos..pos + l] == literal.as_bytes() {
+ RuleResult::Matched(pos + l, ())
+ } else {
+ RuleResult::Failed
+ }
+ }
+}
+
+impl<'input> ParseSlice<'input> for str {
+ type Slice = &'input str;
+ fn parse_slice(&'input self, p1: usize, p2: usize) -> &'input str {
+ &self[p1..p2]
+ }
+}