├── parze-declare ├── .gitignore ├── src │ ├── expr.rs │ ├── lib.rs │ └── rule.rs ├── Cargo.toml └── tests │ └── basic.rs ├── src ├── pad.rs ├── reduce.rs ├── error.rs ├── fail.rs ├── repeat.rs ├── chain.rs ├── parse_fn.rs └── lib.rs ├── benches ├── small_sample.json ├── json.rs └── sample.json ├── Cargo.toml ├── LICENSE-MIT ├── tests ├── macro.rs └── basic.rs ├── examples ├── morse.rs ├── expr.rs ├── json.rs └── custom_error.rs ├── README.md └── LICENSE-APACHE /parze-declare/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /src/pad.rs: -------------------------------------------------------------------------------- 1 | /// A trait to interpret symbols as padding. 2 | /// 3 | /// Implement this trait for your own types to allow padding-related functionality 4 | pub trait Padded { 5 | fn is_padding(&self) -> bool; 6 | } 7 | 8 | impl Padded for char { 9 | fn is_padding(&self) -> bool { 10 | self.is_whitespace() 11 | } 12 | } 13 | 14 | impl Padded for u8 { 15 | fn is_padding(&self) -> bool { 16 | self.is_ascii_whitespace() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /parze-declare/src/expr.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenTree; 2 | use crate::{ 3 | TokenIter, 4 | Error, 5 | attempt, 6 | }; 7 | 8 | pub(crate) fn parse_atom_expr(stream: &mut impl TokenIter) -> Result { 9 | attempt(stream, |stream| { 10 | let tt = stream.next().ok_or(Error::ExpectedAtom)?; 11 | match &tt { 12 | TokenTree::Group(_) => Ok(tt), 13 | TokenTree::Literal(_) => Ok(tt), 14 | TokenTree::Ident(_) => Ok(tt), 15 | _ => Err(Error::ExpectedAtom), 16 | } 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /benches/small_sample.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "glossary": { 4 | "title": "example glossary", 5 | "GlossDiv": { 6 | "title": "S", 7 | "GlossList": { 8 | "GlossEntry": { 9 | "ID": "SGML", 10 | "SortAs": "SGML", 11 | "GlossTerm": "Standard Generalized Markup Language", 12 | "Acronym": "SGML", 13 | "Abbrev": "ISO 8879:1986", 14 | "GlossDef": { 15 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 16 | "GlossSeeAlso": ["GML", "XML"] 17 | }, 18 | "GlossSee": "markup" 19 | } 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /parze-declare/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parze-declare" 3 | version = "0.2.0" 4 | description = "A clean, efficient parser combinator" 5 | authors = ["Joshua Barretto "] 6 | documentation = "https://docs.rs/parze-declare" 7 | repository = "https://www.github.com/zesterer/parze" 8 | homepage = "https://www.github.com/zesterer/parze" 9 | keywords = ["parse", "token", "symbol", "no_std"] 10 | categories = ["parsing", "no-std"] 11 | license = "MIT" 12 | edition = "2018" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [dependencies] 20 | 21 | [dev-dependencies] 22 | parze = { path = ".." } 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parze" 3 | version = "0.7.3" 4 | description = "A clean, efficient parser combinator" 5 | authors = ["Joshua Barretto "] 6 | documentation = "https://docs.rs/parze" 7 | repository = "https://www.github.com/zesterer/parze" 8 | homepage = "https://www.github.com/zesterer/parze" 9 | keywords = ["parse", "token", "symbol", "no_std"] 10 | categories = ["parsing", "no-std"] 11 | readme = "README.md" 12 | license = "MIT" 13 | edition = "2018" 14 | 15 | [features] 16 | macros = ["nightly"] 17 | nightly = [] 18 | default = ["nightly", "macros"] 19 | 20 | [dependencies] 21 | parze-declare = { version = "0.2", path = "parze-declare" } 22 | 23 | [dev-dependencies] 24 | pom = "3.0" 25 | -------------------------------------------------------------------------------- /parze-declare/tests/basic.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_hygiene)] 2 | 3 | use parze::prelude::*; 4 | 5 | #[test] 6 | fn basic() { 7 | #[derive(Clone, Debug, PartialEq)] 8 | enum Instr { 9 | Add, 10 | Sub, 11 | Left, 12 | Right, 13 | In, 14 | Out, 15 | Loop(Vec), 16 | } 17 | 18 | let bf: Parser<_, _> = recursive(|bf| rule! { 19 | ( '+' -> { Instr::Add } 20 | | '-' -> { Instr::Sub } 21 | | '<' -> { Instr::Left } 22 | | '>' -> { Instr::Right } 23 | | ',' -> { Instr::In } 24 | | '.' -> { Instr::Out } 25 | | '[' -& bf &- ']' => { |i| Instr::Loop(i) } 26 | )* 27 | }); 28 | 29 | assert_eq!( 30 | bf.parse("[-]++++[->++++<][->++++<][->++++<].".chars().collect::>()), 31 | Ok(vec![Instr::Add]) 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/reduce.rs: -------------------------------------------------------------------------------- 1 | // ReduceLeft 2 | 3 | pub trait ReduceLeft { 4 | fn reduce_left(self, f: impl Fn(U, T) -> T) -> T; 5 | } 6 | 7 | impl> ReduceLeft for (I, T) { 8 | fn reduce_left(self, f: impl Fn(U, T) -> T) -> T { self.0.into_iter().fold(self.1, |a, b| f(b, a)) } 9 | } 10 | 11 | /* 12 | impl ReduceLeft for (T, U) { 13 | fn reduce_left(self, f: impl Fn(U, T) -> T) -> T { f(self.1, self.0) } 14 | } 15 | */ 16 | 17 | // ReduceRight 18 | 19 | pub trait ReduceRight { 20 | fn reduce_right(self, f: impl Fn(T, U) -> T) -> T; 21 | } 22 | 23 | impl> ReduceRight for (T, I) { 24 | fn reduce_right(self, f: impl Fn(T, U) -> T) -> T { self.1.into_iter().fold(self.0, f) } 25 | } 26 | 27 | /* 28 | impl ReduceRight for (T, U) { 29 | fn reduce_right(self, f: impl Fn(T, U) -> T) -> T { f(self.0, self.1) } 30 | } 31 | */ 32 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Joshua Barretto 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /tests/macro.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_hygiene)] 2 | 3 | use parze::prelude::*; 4 | 5 | #[derive(Clone, Debug, PartialEq)] 6 | enum Instr { 7 | Add, 8 | Sub, 9 | Left, 10 | Right, 11 | In, 12 | Out, 13 | Loop(Vec), 14 | } 15 | 16 | #[test] 17 | #[allow(unused_variables)] 18 | fn simple() { 19 | parsers! { 20 | sym: Parser = { '+' } 21 | 22 | or: Parser = { '+' | '-' } 23 | 24 | then: Parser = { '+' & '-' } 25 | 26 | repeat_at_least_once: Parser = { ('+' & '-')+ } 27 | 28 | repeater_4: Parser = { '+'* } 29 | 30 | mapper: Parser = { '+' => { |c| format!("{}", c) } } 31 | 32 | bf: Parser = { 33 | ( '+' -> { Instr::Add } 34 | | '-' -> { Instr::Sub } 35 | | '<' -> { Instr::Left } 36 | | '>' -> { Instr::Right } 37 | | ',' -> { Instr::In } 38 | | '.' -> { Instr::Out } 39 | | '[' -& bf &- ']' => { |i| Instr::Loop(i) } 40 | )* 41 | } 42 | } 43 | 44 | bf.parse(&"++[,>>]++-".chars().collect::>()).unwrap(); 45 | } 46 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | /// A trait to be implemented by parser errors. 2 | /// The default implementation of this trait is `DefaultParseError`. 3 | /// You may implement it yourself to have custom errors be generated by the parser. 4 | pub trait ParseError: Sized { 5 | /// Create an error that indicates that the given symbol was not expected by the parser. 6 | fn unexpected(token: T) -> Self; 7 | 8 | /// Create an error that indicates that the symbol input unexpectedly ended. 9 | fn unexpected_end() -> Self; 10 | 11 | /// Combine two errors of equal priority 12 | fn combine(self, _other: Self) -> Self { self } 13 | } 14 | 15 | /// The default implementation of `ParseError`. 16 | /// Unless specified, parsers will use this type as their error type. 17 | #[derive(Clone, Debug, PartialEq)] 18 | pub enum DefaultParseError { 19 | /// A symbol was unexpected. 20 | Unexpected(T), 21 | /// The symbol input unexpectedly ended. 22 | UnexpectedEnd, 23 | } 24 | 25 | impl ParseError for DefaultParseError { 26 | fn unexpected(token: T) -> Self { 27 | DefaultParseError::Unexpected(token) 28 | } 29 | 30 | fn unexpected_end() -> Self { 31 | DefaultParseError::UnexpectedEnd 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /parze-declare/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_def_site, proc_macro_quote, proc_macro_hygiene, trait_alias)] 2 | 3 | mod rule; 4 | mod expr; 5 | 6 | extern crate proc_macro; 7 | 8 | use proc_macro::{TokenStream, TokenTree}; 9 | 10 | #[derive(Debug)] 11 | enum Error { 12 | UnexpectedToken, 13 | ExpectedAtom, 14 | ExpectedPunct, 15 | } 16 | 17 | trait TokenStreamExt { 18 | fn and(self, other: TokenStream) -> TokenStream; 19 | } 20 | 21 | impl TokenStreamExt for TokenStream { 22 | fn and(mut self, other: TokenStream) -> TokenStream { 23 | self.extend(Some(other)); 24 | self 25 | } 26 | } 27 | 28 | impl TokenStreamExt for TokenTree { 29 | fn and(self, other: TokenStream) -> TokenStream { 30 | let mut this = TokenStream::from(self); 31 | this.extend(Some(other)); 32 | this 33 | } 34 | } 35 | 36 | trait TokenIter = Iterator + Clone; 37 | 38 | fn attempt<'a, 'c: 'a, I, R, E, F>(iter: &mut I, f: F) -> Result 39 | where 40 | I: TokenIter, 41 | F: FnOnce(&mut I) -> Result, 42 | { 43 | let mut iter2 = iter.clone(); 44 | let tok = f(&mut iter2)?; 45 | *iter = iter2; 46 | Ok(tok) 47 | } 48 | 49 | #[proc_macro] 50 | pub fn rule(stream: TokenStream) -> TokenStream { 51 | rule::parse_rule(&mut stream.into_iter()).unwrap().into() 52 | } 53 | -------------------------------------------------------------------------------- /tests/basic.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | #![feature(proc_macro_hygiene)] 3 | 4 | use parze::prelude::*; 5 | 6 | #[test] 7 | fn brainfuck() { 8 | #[derive(Clone, Debug, PartialEq)] 9 | enum Instr { 10 | Add, 11 | Sub, 12 | Left, 13 | Right, 14 | In, 15 | Out, 16 | Loop(Vec), 17 | } 18 | 19 | parsers! { 20 | bf: Parser<_, _> = { 21 | ( '+' -> { Instr::Add } 22 | | '-' -> { Instr::Sub } 23 | | '<' -> { Instr::Left } 24 | | '>' -> { Instr::Right } 25 | | ',' -> { Instr::In } 26 | | '.' -> { Instr::Out } 27 | | '[' -& bf &- ']' => { |i| Instr::Loop(i) } 28 | )* 29 | } 30 | } 31 | 32 | let program_src = "++++[->++++<]>[->++++<]>."; 33 | 34 | let program_tgt = vec![ // Prints '@' 35 | Instr::Add, 36 | Instr::Add, 37 | Instr::Add, 38 | Instr::Add, 39 | Instr::Loop(vec![ 40 | Instr::Sub, 41 | Instr::Right, 42 | Instr::Add, 43 | Instr::Add, 44 | Instr::Add, 45 | Instr::Add, 46 | Instr::Left, 47 | ]), 48 | Instr::Right, 49 | Instr::Loop(vec![ 50 | Instr::Sub, 51 | Instr::Right, 52 | Instr::Add, 53 | Instr::Add, 54 | Instr::Add, 55 | Instr::Add, 56 | Instr::Left, 57 | ]), 58 | Instr::Right, 59 | Instr::Out, 60 | ]; 61 | 62 | assert_eq!(bf.parse(&program_src.chars().collect::>()), Ok(program_tgt)); 63 | } 64 | -------------------------------------------------------------------------------- /src/fail.rs: -------------------------------------------------------------------------------- 1 | use crate::error::ParseError; 2 | 3 | #[derive(Debug)] 4 | pub struct Fail(usize, E); 5 | 6 | impl Fail { 7 | pub fn new(idx: usize, err: E) -> Self { 8 | Self(idx, err) 9 | } 10 | 11 | pub fn take_error(self) -> E { 12 | self.1 13 | } 14 | 15 | pub fn map_err(self, f: impl Fn(E) -> G) -> Fail { 16 | Fail(self.0, f(self.1)) 17 | } 18 | 19 | #[inline(always)] 20 | pub fn max(self, other: impl Into>) -> Self where E: ParseError { 21 | match other.into().0 { 22 | Some((idx, err)) if idx > self.0 => Self(idx, err), 23 | Some((idx, _)) if idx < self.0 => Self(self.0, self.1), 24 | Some((idx, err)) if idx == self.0 => Self(idx, err.combine(self.1)), 25 | _ => self, 26 | } 27 | } 28 | } 29 | 30 | #[derive(Debug)] 31 | pub struct MayFail(Option<(usize, E)>); 32 | 33 | impl MayFail { 34 | pub fn none() -> Self { 35 | Self(None) 36 | } 37 | 38 | pub fn map_err(self, f: impl Fn(E) -> G) -> MayFail { 39 | MayFail(self.0.map(|(idx, e)| (idx, f(e)))) 40 | } 41 | 42 | #[inline(always)] 43 | pub fn max(self, other: impl Into>) -> Self where E: ParseError { 44 | match (self.0, other.into().0) { 45 | (Some(a), Some(b)) => Fail(a.0, a.1).max(Fail(b.0, b.1)).into(), 46 | (Some((a_idx, a)), _) => Self(Some((a_idx, a))), 47 | (_, Some((b_idx, b))) => Self(Some((b_idx, b))), 48 | _ => Self(None), 49 | } 50 | } 51 | } 52 | 53 | impl From> for MayFail { 54 | fn from(fail: Fail) -> Self { 55 | Self(Some((fail.0, fail.1))) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/morse.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | #![feature(proc_macro_hygiene)] 3 | #![recursion_limit="256"] 4 | #![type_length_limit="100000000"] 5 | 6 | use parze::prelude::*; 7 | 8 | fn main() { 9 | parsers!{ 10 | morse: Parser<_, _> = { 11 | ( 12 | ( { all_of(b"-...") } -> 'B' 13 | | { all_of(b"-.-.") } -> 'C' 14 | | { all_of(b"..-.") } -> 'F' 15 | | { all_of(b"....") } -> 'H' 16 | | { all_of(b".---") } -> 'J' 17 | | { all_of(b".-..") } -> 'L' 18 | | { all_of(b".--.") } -> 'P' 19 | | { all_of(b"--.-") } -> 'Q' 20 | | { all_of(b"...-") } -> 'V' 21 | | { all_of(b"-..-") } -> 'X' 22 | | { all_of(b"-.--") } -> 'Y' 23 | | { all_of(b"--..") } -> 'Z' 24 | | { all_of(b"-..") } -> 'D' 25 | | { all_of(b"--.") } -> 'G' 26 | | { all_of(b"-.-") } -> 'K' 27 | | { all_of(b"---") } -> 'O' 28 | | { all_of(b".-.") } -> 'R' 29 | | { all_of(b"...") } -> 'S' 30 | | { all_of(b"..-") } -> 'U' 31 | | { all_of(b".--") } -> 'W' 32 | | { all_of(b".-") } -> 'A' 33 | | { all_of(b"..") } -> 'I' 34 | | { all_of(b"--") } -> 'M' 35 | | { all_of(b"-.") } -> 'N' 36 | | { all_of(b".") } -> 'E' 37 | | { all_of(b"-") } -> 'T' 38 | )~ 39 | ) * => { |cs| cs.collect::() } 40 | } 41 | } 42 | 43 | println!("{}", morse.parse(b".... . .-.. .-.. --- .-- . .-.. -.-. --- -- . - --- .--. .- .-. --.. .").unwrap()); 44 | } 45 | -------------------------------------------------------------------------------- /examples/expr.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | #![feature(proc_macro_hygiene)] 3 | 4 | use parze::prelude::*; 5 | 6 | #[derive(Debug)] 7 | enum Expr { 8 | Literal(i64), 9 | Neg(Box), 10 | Mul(Box, Box), 11 | Div(Box, Box), 12 | Rem(Box, Box), 13 | Add(Box, Box), 14 | Sub(Box, Box), 15 | } 16 | 17 | impl Expr { 18 | fn eval(&self) -> i64 { 19 | match self { 20 | Expr::Literal(a) => *a, 21 | Expr::Neg(a) => -a.eval(), 22 | Expr::Mul(a, b) => a.eval() * b.eval(), 23 | Expr::Div(a, b) => a.eval() / b.eval(), 24 | Expr::Rem(a, b) => a.eval() % b.eval(), 25 | Expr::Add(a, b) => a.eval() + b.eval(), 26 | Expr::Sub(a, b) => a.eval() - b.eval(), 27 | } 28 | } 29 | } 30 | 31 | fn main() { 32 | parsers! { 33 | number = { 34 | { one_of("0123456789".chars()) }+ => { |s| Expr::Literal(s.collect::().parse().unwrap()) } 35 | } 36 | 37 | atom = { 38 | ( number | '(' -& expr &- ')')~ 39 | } 40 | 41 | unary = { 42 | '-'~* & atom <: { |_, e| Expr::Neg(e.into()) } 43 | } 44 | 45 | product = { 46 | unary & (('*' | '/' | '%')~ & unary)* :> { |a, (op, b)| match op { 47 | '*' => Expr::Mul(a.into(), b.into()), 48 | '/' => Expr::Div(a.into(), b.into()), 49 | '%' => Expr::Rem(a.into(), b.into()), 50 | _ => unreachable!(), 51 | }} 52 | } 53 | 54 | sum = { 55 | product & (('+' | '-')~ & product)* :> {|a, (op, b)| match op { 56 | '+' => Expr::Add(a.into(), b.into()), 57 | '-' => Expr::Sub(a.into(), b.into()), 58 | _ => unreachable!(), 59 | }} 60 | } 61 | 62 | expr: Parser<_, _> = { ' '* -& sum } 63 | } 64 | 65 | assert_eq!( 66 | expr.parse_str("14 + 3 / 1 * (2 + 4) + -1").unwrap().eval(), 67 | 31, 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /src/repeat.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Range, RangeFull, RangeFrom, RangeTo}; 2 | 3 | /// A type that represents possible pattern repetitions. 4 | #[derive(Copy, Clone)] 5 | pub enum Repeat { 6 | /// The pattern may be seen any number of times (including not at all). 7 | Any, 8 | /// The pattern must be seen a specific number of times. 9 | Exactly(usize), 10 | /// The pattern must be seen at least a specific number of times. 11 | AtLeast(usize), 12 | /// The pattern must be seen at most a specific number of times. 13 | AtMost(usize), 14 | /// The pattern must be seen at least a specific number of times and at most a specific number of times. 15 | Range(usize, usize), 16 | } 17 | 18 | impl From for Repeat { 19 | fn from(n: usize) -> Self { 20 | Repeat::Exactly(n) 21 | } 22 | } 23 | 24 | impl From for Repeat { 25 | fn from(_: RangeFull) -> Self { 26 | Repeat::Any 27 | } 28 | } 29 | 30 | impl From> for Repeat { 31 | fn from(from: Range) -> Self { 32 | Repeat::Range(from.start, from.end) 33 | } 34 | } 35 | 36 | impl From> for Repeat { 37 | fn from(from: RangeFrom) -> Self { 38 | Repeat::AtLeast(from.start) 39 | } 40 | } 41 | 42 | impl From> for Repeat { 43 | fn from(from: RangeTo) -> Self { 44 | Repeat::AtMost(from.end) 45 | } 46 | } 47 | 48 | impl Repeat { 49 | /// Returns true if this repetition permits the given number of repetitions. 50 | pub fn contains(self, m: usize) -> bool { 51 | match self { 52 | Repeat::Any => true, 53 | Repeat::Exactly(n) if m == n => true, 54 | Repeat::AtLeast(n) if m >= n => true, 55 | Repeat::AtMost(n) if m <= n => true, 56 | Repeat::Range(a, b) if m >= a && m <= b => true, 57 | _ => false, 58 | } 59 | } 60 | 61 | pub(crate) fn idx_upper_limit(self) -> usize { 62 | match self { 63 | Repeat::Any => !0, 64 | Repeat::Exactly(n) => n, 65 | Repeat::AtLeast(_) => !0, 66 | Repeat::AtMost(n) => n, 67 | Repeat::Range(_, b) => b, 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/json.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | #![feature(proc_macro_hygiene)] 3 | 4 | use parze::prelude::*; 5 | use std::collections::HashMap; 6 | 7 | #[derive(Debug, PartialEq)] 8 | pub enum JsonValue { 9 | Null, 10 | Bool(bool), 11 | Str(String), 12 | Num(f64), 13 | Array(Vec), 14 | Object(HashMap) 15 | } 16 | 17 | fn main() { 18 | parsers! { 19 | integer = { { one_of("123456789".chars()) }.% % { one_of("0123456789".chars()) }* |% '0'.% } 20 | frac = { '.'.% % { one_of("0123456789".chars()) }+ } 21 | exp = { ('e' | 'E').% % ('+' | '-')? % { one_of("0123456789".chars()) }+ } 22 | number = { '-'? % integer % frac?.# % exp?.# => { |cs| cs.collect::().parse().unwrap() } } 23 | 24 | special = { '\\' | '/' | '"' | 'b' -> '\x08' | 'f' -> '\x0C' | 'n' -> '\n' | 'r' -> '\r' | 't' -> '\t' } 25 | escape = { '\\' -& special } 26 | string = { '"' -& ({ none_of("\\\"".chars()) } | escape)* &- '"' => { |cs| cs.collect::() } } 27 | 28 | elements = { value ... ','~ } 29 | array = { '['~ -& elements &- ']' } 30 | 31 | member = { string~ &- ':'~ & value } 32 | members = { member ... ','~ } 33 | 34 | object = { '{'~ -& members &- '}' => { |m| m.collect() } } 35 | 36 | value: Parser<_, _> = { 37 | ~( 38 | | { all_of("null".chars()) } => { |_| JsonValue::Null } 39 | | { all_of("true".chars()) } => { |_| JsonValue::Bool(true) } 40 | | { all_of("false".chars()) } => { |_| JsonValue::Bool(false) } 41 | | number => { |n| JsonValue::Num(n) } 42 | | string => { |s| JsonValue::Str(s) } 43 | | array => { |a| JsonValue::Array(a) } 44 | | object => { |o| JsonValue::Object(o) } 45 | )~ 46 | } 47 | } 48 | 49 | let test_json = r#" 50 | { 51 | "parze": { 52 | "description": "parser combinator library", 53 | "has_macros": true 54 | }, 55 | "some_numbers": [42, 13.37, 256], 56 | "hypothesis": null 57 | } 58 | "#; 59 | 60 | println!("{:#?}", value.parse_str(test_json)); 61 | } 62 | -------------------------------------------------------------------------------- /src/chain.rs: -------------------------------------------------------------------------------- 1 | use core::iter::FromIterator; 2 | 3 | pub trait Chain: Sized { 4 | type Item; 5 | type IntoIter: IntoIterator; 6 | fn into_iter_chain(self) -> ::IntoIter; 7 | fn collect>(self) -> T { self.into_iter_chain().collect() } 8 | fn as_slice(&self) -> &[Self::Item]; 9 | } 10 | 11 | impl Chain for Vec { 12 | type Item = T; 13 | type IntoIter = ::IntoIter; 14 | fn into_iter_chain(self) -> ::IntoIter { IntoIterator::into_iter(self) } 15 | fn as_slice(&self) -> &[Self::Item] { &self } 16 | } 17 | 18 | pub struct Single([T; 1]); 19 | 20 | impl From for Single { 21 | fn from(item: T) -> Self { 22 | Self([item]) 23 | } 24 | } 25 | 26 | impl IntoIterator for Single { 27 | type Item = T; 28 | type IntoIter = SingleIter; 29 | 30 | fn into_iter(self) -> Self::IntoIter { 31 | let [inner] = self.0; 32 | SingleIter(Some(inner)) 33 | } 34 | } 35 | 36 | impl Chain for Single { 37 | type Item = T; 38 | type IntoIter = ::IntoIter; 39 | fn into_iter_chain(self) -> ::IntoIter { IntoIterator::into_iter(self) } 40 | fn as_slice(&self) -> &[Self::Item] { &self.0 } 41 | } 42 | 43 | pub struct SingleIter(Option); 44 | 45 | impl Iterator for SingleIter { 46 | type Item = T; 47 | 48 | fn next(&mut self) -> Option { 49 | self.0.take() 50 | } 51 | } 52 | 53 | pub trait IntoChain { 54 | type Item; 55 | type Chain: Chain; 56 | 57 | fn into_chain(self) -> Self::Chain; 58 | } 59 | 60 | impl IntoChain for U { 61 | type Item = U::Item; 62 | type Chain = Self; 63 | fn into_chain(self) -> Self::Chain { self } 64 | } 65 | 66 | impl IntoChain for Option { 67 | type Item = T; 68 | type Chain = OptionChain; 69 | 70 | fn into_chain(self) -> Self::Chain { 71 | OptionChain(self.map(|item| [item])) 72 | } 73 | } 74 | 75 | pub struct OptionChain(Option<[T; 1]>); 76 | 77 | impl Chain for OptionChain { 78 | type Item = T; 79 | type IntoIter = ::IntoIter; 80 | fn into_iter_chain(self) -> ::IntoIter { IntoIterator::into_iter(self) } 81 | fn as_slice(&self) -> &[Self::Item] { 82 | self.0.as_ref().map(|arr| arr.as_ref()).unwrap_or(&[]) 83 | } 84 | } 85 | 86 | impl IntoIterator for OptionChain { 87 | type Item = T; 88 | type IntoIter = SingleIter; 89 | 90 | fn into_iter(self) -> Self::IntoIter { 91 | self.0.map(|[inner]| { 92 | SingleIter(Some(inner)) 93 | }).unwrap_or_else(|| SingleIter(None)) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/v/parze.svg)](https://crates.io/crates/parze) 2 | [![crates.io](https://docs.rs/parze/badge.svg)](https://docs.rs/parze) 3 | 4 | **Parze is now deprecated** 5 | 6 | *Take a look at [chumsky](https://github.com/zesterer/chumsky/), a from-scratch reimplementation of parze with more features, better performance, and a cleaner API.* 7 | 8 | # Parze 9 | 10 | Parze is a clean, efficient parser combinator written in Rust. 11 | 12 | ## Example 13 | 14 | A parser capable of parsing all valid Brainfuck code into an AST. 15 | 16 | ```rust 17 | use parze::prelude::*; 18 | 19 | #[derive(Clone, Debug, PartialEq)] 20 | enum Instr { Add, Sub, Left, Right, In, Out, Loop(Vec) } 21 | 22 | parsers! { 23 | bf = { 24 | ( '+' -> { Instr::Add } 25 | | '-' -> { Instr::Sub } 26 | | '<' -> { Instr::Left } 27 | | '>' -> { Instr::Right } 28 | | ',' -> { Instr::In } 29 | | '.' -> { Instr::Out } 30 | | '[' -& bf &- ']' => { |i| Instr::Loop(i) } 31 | ) * 32 | } 33 | } 34 | ``` 35 | 36 | ## Features 37 | 38 | - [x] All the usual parser combinator operations 39 | - [x] Macro for simple rule and parser declaration 40 | - [x] Support for recursive parser definitions 41 | - [x] Custom error types - define your own! 42 | - [x] Prioritised / merged failure for more useful errors 43 | - [x] No dependencies - fast compilation! 44 | - [x] `no_std` support 45 | 46 | ## Why Parze? 47 | 48 | Parze is fast and lightweight, acting as a bare-bones framework upon which more verbose and interesting parsers can be constructed (see the `custom_error` example). 49 | 50 | ## Nightly 51 | 52 | Parze's declaration macro currently requires a nightly Rust compiler to work. 53 | You may use the explicit declaration form (as shown below) with stable by disabling the `nightly` feature, however. 54 | 55 | This can be done like so in your `Cargo.toml`: 56 | 57 | ``` 58 | [dependencies.parze] 59 | version = "x.y.z" 60 | default-features = false 61 | ``` 62 | 63 | ## Performance 64 | 65 | Here are the results of a JSON parsing test when compared with [`pom`](https://github.com/J-F-Liu/pom). More performance metrics to come later. 66 | 67 | ``` 68 | test parze ... bench: 3,696,323 ns/iter (+/- 358,597) 69 | test pom ... bench: 18,538,775 ns/iter (+/- 1,149,589) 70 | ``` 71 | 72 | ## Explicit Form 73 | 74 | While Parze encourages use of macros for much of its declarative notation, it is possible (and often useful) to make use of the more explicit rust-y notation. 75 | 76 | Here is the Brainfuck parser given above, declared in explicit form. 77 | 78 | ```rust 79 | let bf: Parser<_, _> = recursive(|bf| ( 80 | sym('+').to(Instr::Add) 81 | .or(sym('-').to(Instr::Sub)) 82 | .or(sym('<').to(Instr::Left)) 83 | .or(sym('>').to(Instr::Right)) 84 | .or(sym(',').to(Instr::In)) 85 | .or(sym('.').to(Instr::Out)) 86 | .or(sym('[').delimiter_for(bf).delimited_by(sym(']')).map(|i| Instr::Loop(i))) 87 | ).repeat(..)); 88 | ``` 89 | 90 | ## License 91 | 92 | Parze is distributed under either of: 93 | 94 | - Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 95 | 96 | - MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) 97 | 98 | at the discretion of the user. 99 | -------------------------------------------------------------------------------- /examples/custom_error.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | #![feature(proc_macro_hygiene)] 3 | 4 | use parze::prelude::*; 5 | use std::collections::HashSet; 6 | 7 | type Token = (usize, char); 8 | 9 | #[derive(Debug)] 10 | struct BrainfuckError { 11 | found: Option, 12 | expected: HashSet, // A list of expected symbols, assembled during error propagation 13 | } 14 | 15 | impl BrainfuckError { 16 | // This is pretty poor code, you can do better 17 | fn print(&self, code: &str) { 18 | println!("1 | {}", code); 19 | 20 | print!(" | "); 21 | for _ in 0..self.found.map(|(idx, _)| idx).unwrap_or(code.len()) { 22 | print!(" "); 23 | } 24 | println!("^"); 25 | 26 | let expected_str = self.expected 27 | .iter() 28 | .map(|c| format!("'{}'", c)) 29 | .collect::>() 30 | .join(", "); 31 | match self.found { 32 | Some((idx, c)) => println!("Error at column {}: Unexpected token '{}'.", idx + 1, c), 33 | None => println!("Error: Unexpected end of file."), 34 | } 35 | 36 | match self.expected.len() { 37 | 0 => {}, 38 | 1 => println!("Note: Expected {}.", expected_str), 39 | _ => println!("Note: Expected one of {}.", expected_str), 40 | } 41 | } 42 | } 43 | 44 | impl ParseError for BrainfuckError { 45 | fn unexpected(token: Token) -> Self { 46 | Self { found: Some(token), expected: HashSet::default() } 47 | } 48 | 49 | fn unexpected_end() -> Self { 50 | Self { found: None, expected: HashSet::default() } 51 | } 52 | 53 | // Combine two same-priority errors together 54 | fn combine(mut self, other: Self) -> Self { 55 | self.expected = self.expected 56 | .union(&other.expected) 57 | .copied() 58 | .collect(); 59 | self 60 | } 61 | } 62 | 63 | #[derive(Clone, Debug, PartialEq)] 64 | enum Instr { 65 | Add, 66 | Sub, 67 | Left, 68 | Right, 69 | In, 70 | Out, 71 | Loop(Vec), 72 | } 73 | 74 | // A parser that only accepts tokens with matching characters and produces an "Expected 'c'" error 75 | fn expect<'a>(c: char) -> Parser<'a, Token, (), BrainfuckError> { 76 | permit(move |(_, found_c)| found_c == c) 77 | .discard() 78 | .map_err(move |mut err: BrainfuckError| { 79 | // Add the character to the list of expected characters if an error occurred 80 | err.expected.insert(c); 81 | err 82 | }) 83 | .to_object() 84 | } 85 | 86 | fn main() { 87 | parsers! { 88 | bf: Parser<_, _, BrainfuckError> = { 89 | ( { expect('+') } -> { Instr::Add } 90 | | { expect('-') } -> { Instr::Sub } 91 | | { expect('<') } -> { Instr::Left } 92 | | { expect('>') } -> { Instr::Right } 93 | | { expect(',') } -> { Instr::In } 94 | | { expect('.') } -> { Instr::Out } 95 | | { expect('[') } -& bf &- { expect(']') } => { |i| Instr::Loop(i) } 96 | )* 97 | } 98 | } 99 | 100 | // This code contains an error 101 | let code = "+++!+[->++++<]>[->++++<]."; 102 | 103 | let error = bf.parse(&code.chars().enumerate().collect::>()).unwrap_err(); 104 | 105 | error.print(code); 106 | } 107 | -------------------------------------------------------------------------------- /benches/json.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "macros")] 2 | #![feature(proc_macro_hygiene)] 3 | 4 | #![feature(test)] 5 | 6 | extern crate test; 7 | 8 | use std::collections::HashMap; 9 | use test::{Bencher, black_box}; 10 | 11 | #[derive(Debug, PartialEq)] 12 | pub enum JsonValue { 13 | Null, 14 | Bool(bool), 15 | Str(String), 16 | Num(f64), 17 | Array(Vec), 18 | Object(HashMap) 19 | } 20 | 21 | #[bench] 22 | fn parze(b: &mut Bencher) { 23 | let json = parze::json(); 24 | b.iter(|| black_box(json.parse(include_bytes!("sample.json")).unwrap())); 25 | //dbg!(parze::json().parse(include_bytes!("small_sample.json"))); 26 | } 27 | 28 | #[bench] 29 | fn pom(b: &mut Bencher) { 30 | let json = pom::json(); 31 | b.iter(|| black_box(json.parse(include_bytes!("sample.json")).unwrap())); 32 | } 33 | 34 | mod parze { 35 | use parze::prelude::*; 36 | 37 | use std::str; 38 | use super::JsonValue; 39 | 40 | pub fn json() -> Parser<'static, u8, JsonValue> { 41 | parsers! { 42 | integer = { { one_of(b"123456789") }.% % { one_of(b"0123456789") }* |% b'0'.% } 43 | frac = { b'.'.% % { one_of(b"0123456789") }+ } 44 | exp = { (b'e' | b'E').% % (b'+' | b'-')? % { one_of(b"0123456789") }+ } 45 | number: Parser<_, _, _, _> = { b'-'? % integer % frac?.# % exp?.# => { |b| str::from_utf8(&b.as_slice()).unwrap().parse().unwrap() } } 46 | 47 | special = { b'\\' | b'/' | b'"' | b'b' -> b'\x08' | b'f' -> b'\x0C' | b'n' -> b'\n' | b'r' -> b'\r' | b't' -> b'\t' } 48 | escape = { b'\\' -& special } 49 | string = { b'"' -& ({ none_of(b"\\\"") } | escape)* &- b'"' => { |b| String::from_utf8(b).unwrap() } } 50 | 51 | elements = { value ... b','~ } 52 | array = { b'['~ -& elements &- b']' } 53 | 54 | member = { string~ &- b':'~ & value } 55 | members = { member ... b','~ } 56 | 57 | object = { b'{'~ -& members &- b'}' => { |m| m.collect() } } 58 | 59 | value = { 60 | ~( 61 | | { all_of(b"null") } => { |_| JsonValue::Null } 62 | | { all_of(b"true") } => { |_| JsonValue::Bool(true) } 63 | | { all_of(b"false") } => { |_| JsonValue::Bool(false) } 64 | | number => { |n| JsonValue::Num(n) } 65 | | string => { |s| JsonValue::Str(s) } 66 | | array => { |a| JsonValue::Array(a) } 67 | | object => { |o| JsonValue::Object(o) } 68 | )~ 69 | } 70 | } 71 | 72 | value 73 | } 74 | } 75 | 76 | mod pom { 77 | use pom::parser::*; 78 | use pom::Parser; 79 | 80 | use std::collections::HashMap; 81 | use std::str::{self, FromStr}; 82 | use super::JsonValue; 83 | 84 | fn space() -> Parser { 85 | one_of(b" \t\r\n").repeat(0..).discard() 86 | } 87 | 88 | fn number() -> Parser { 89 | let integer = one_of(b"123456789") - one_of(b"0123456789").repeat(0..) | sym(b'0'); 90 | let frac = sym(b'.') + one_of(b"0123456789").repeat(1..); 91 | let exp = one_of(b"eE") + one_of(b"+-").opt() + one_of(b"0123456789").repeat(1..); 92 | let number = sym(b'-').opt() + integer + frac.opt() + exp.opt(); 93 | number.collect().convert(str::from_utf8).convert(|s|f64::from_str(&s)) 94 | } 95 | 96 | fn string() -> Parser { 97 | let special_char = sym(b'\\') | sym(b'/') | sym(b'"') 98 | | sym(b'b').map(|_|b'\x08') | sym(b'f').map(|_|b'\x0C') 99 | | sym(b'n').map(|_|b'\n') | sym(b'r').map(|_|b'\r') | sym(b't').map(|_|b'\t'); 100 | let escape_sequence = sym(b'\\') * special_char; 101 | let string = sym(b'"') * (none_of(b"\\\"") | escape_sequence).repeat(0..) - sym(b'"'); 102 | string.convert(String::from_utf8) 103 | } 104 | 105 | fn array() -> Parser> { 106 | let elems = list(call(value), sym(b',') * space()); 107 | sym(b'[') * space() * elems - sym(b']') 108 | } 109 | 110 | fn object() -> Parser> { 111 | let member = string() - space() - sym(b':') - space() + call(value); 112 | let members = list(member, sym(b',') * space()); 113 | let obj = sym(b'{') * space() * members - sym(b'}'); 114 | obj.map(|members|members.into_iter().collect::>()) 115 | } 116 | 117 | fn value() -> Parser { 118 | ( seq(b"null").map(|_|JsonValue::Null) 119 | | seq(b"true").map(|_|JsonValue::Bool(true)) 120 | | seq(b"false").map(|_|JsonValue::Bool(false)) 121 | | number().map(|num|JsonValue::Num(num)) 122 | | string().map(|text|JsonValue::Str(text)) 123 | | array().map(|arr|JsonValue::Array(arr)) 124 | | object().map(|obj|JsonValue::Object(obj)) 125 | ) - space() 126 | } 127 | 128 | pub fn json() -> Parser { 129 | space() * value() - end() 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/parse_fn.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | use alloc::rc::Rc; 3 | use crate::{ 4 | ParseError, 5 | ParseResult, 6 | TokenIter, 7 | chain::{IntoChain, Chain}, 8 | }; 9 | 10 | pub trait ParseFn>: Clone { 11 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult; 12 | } 13 | 14 | // RcParseFn 15 | 16 | pub struct RcParseFn<'a, T: Clone, O, E: ParseError> { 17 | parse: Rc) -> ParseResult + 'a>, 18 | } 19 | 20 | impl<'a, T: Clone, O, E: ParseError> RcParseFn<'a, T, O, E> { 21 | pub fn new(f: impl Fn(&mut TokenIter) -> ParseResult + 'a) -> Self { 22 | Self { parse: Rc::new(f) } 23 | } 24 | } 25 | 26 | impl> Clone for RcParseFn<'_, T, O, E> { 27 | fn clone(&self) -> Self { 28 | Self { parse: self.parse.clone() } 29 | } 30 | } 31 | 32 | impl> ParseFn for RcParseFn<'_, T, O, E> { 33 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult { 34 | (self.parse)(tokens) 35 | } 36 | } 37 | 38 | // GenParseFn 39 | 40 | pub struct GenParseFn, F: Fn(&mut TokenIter) -> ParseResult + Clone> { 41 | parse: F, 42 | _phantom: PhantomData<(T, O, E)>, 43 | } 44 | 45 | impl, F: Fn(&mut TokenIter) -> ParseResult + Clone> GenParseFn { 46 | pub fn new(f: F) -> Self { 47 | Self { 48 | parse: f, 49 | _phantom: PhantomData, 50 | } 51 | } 52 | } 53 | 54 | impl, F: Fn(&mut TokenIter) -> ParseResult + Clone> Clone for GenParseFn { 55 | fn clone(&self) -> Self { 56 | Self { 57 | parse: self.parse.clone(), 58 | _phantom: PhantomData, 59 | } 60 | } 61 | } 62 | 63 | impl, F: Fn(&mut TokenIter) -> ParseResult + Clone> ParseFn for GenParseFn { 64 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult { 65 | (self.parse)(tokens) 66 | } 67 | } 68 | 69 | // ThenFn 70 | 71 | #[derive(Clone)] 72 | pub(crate) struct ThenFn(pub A, pub B); 73 | 74 | impl ParseFn for ThenFn 75 | where 76 | T: Clone, 77 | E: ParseError, 78 | F: ParseFn, 79 | G: ParseFn, 80 | { 81 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult<(O, U), E> { 82 | let (a_fail, a) = self.0.parse(tokens)?; 83 | match self.1.parse(tokens) { 84 | Ok((b_fail, b)) => Ok((a_fail.max(b_fail), (a, b))), 85 | Err(b_fail) => Err(b_fail.max(a_fail)), 86 | } 87 | } 88 | } 89 | 90 | // ChainFn 91 | 92 | pub(crate) struct ChainFn(pub A, pub B, pub PhantomData<(O, U)>); 93 | 94 | impl Clone for ChainFn { 95 | fn clone(&self) -> Self { 96 | Self(self.0.clone(), self.1.clone(), PhantomData) 97 | } 98 | } 99 | 100 | impl ParseFn, E> for ChainFn 101 | where 102 | T: Clone, 103 | O: IntoChain, 104 | U: IntoChain, 105 | E: ParseError, 106 | F: ParseFn, 107 | G: ParseFn, 108 | { 109 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult, E> { 110 | let (a_fail, a) = self.0.parse(tokens)?; 111 | match self.1.parse(tokens) { 112 | Ok((b_fail, b)) => Ok((a_fail.max(b_fail), a.into_chain().into_iter_chain().chain(b.into_chain().into_iter_chain()).collect::>())), 113 | Err(b_fail) => Err(b_fail.max(a_fail)), 114 | } 115 | } 116 | } 117 | 118 | // OrFn 119 | 120 | #[derive(Clone)] 121 | pub(crate) struct OrFn(pub A, pub B); 122 | 123 | impl ParseFn for OrFn 124 | where 125 | T: Clone, 126 | E: ParseError, 127 | F: ParseFn, 128 | G: ParseFn, 129 | { 130 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult { 131 | let mut b_tokens = tokens.clone(); 132 | let a = self.0.parse(tokens); 133 | let b = self.1.parse(&mut b_tokens); 134 | match a { 135 | Ok((a_fail, a)) => match b { 136 | Ok((b_fail, _)) => Ok((a_fail.max(b_fail), a)), 137 | Err(b_fail) => Ok((a_fail.max(b_fail), a)), 138 | }, 139 | Err(a_fail) => match b { 140 | Ok((b_fail, b)) => { 141 | *tokens = b_tokens; 142 | Ok((b_fail.max(a_fail), b)) 143 | }, 144 | Err(b_fail) => Err(a_fail.max(b_fail)), 145 | }, 146 | } 147 | } 148 | } 149 | 150 | // OrFallbackFn 151 | 152 | #[derive(Clone)] 153 | pub(crate) struct OrFallbackFn(pub A, pub B); 154 | 155 | impl ParseFn for OrFallbackFn 156 | where 157 | T: Clone, 158 | E: ParseError, 159 | F: ParseFn, 160 | G: ParseFn, 161 | { 162 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult { 163 | let a = self.0.parse(tokens); 164 | match a { 165 | Ok((a_fail, a)) => Ok((a_fail, a)), 166 | Err(a_fail) => match self.1.parse(tokens) { 167 | Ok((b_fail, b)) => Ok((b_fail.max(a_fail), b)), 168 | Err(b_fail) => Err(a_fail.max(b_fail)), 169 | }, 170 | } 171 | } 172 | } 173 | 174 | 175 | // OrChainFn 176 | 177 | pub struct OrChainFn(pub A, pub B, pub PhantomData<(O, U)>); 178 | 179 | impl Clone for OrChainFn { 180 | fn clone(&self) -> Self { 181 | Self(self.0.clone(), self.1.clone(), PhantomData) 182 | } 183 | } 184 | 185 | impl ParseFn, E> for OrChainFn 186 | where 187 | T: Clone, 188 | O: IntoChain, 189 | U: IntoChain, 190 | E: ParseError, 191 | F: ParseFn, 192 | G: ParseFn, 193 | { 194 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult, E> { 195 | let a = self.0.parse(tokens); 196 | let mut b_tokens = tokens.clone(); 197 | let b = self.1.parse(&mut b_tokens); 198 | match a { 199 | Ok((a_fail, a)) => match b { 200 | Ok((b_fail, _)) => Ok((a_fail.max(b_fail), a.into_chain().into_iter_chain().collect::>())), 201 | Err(b_fail) => Ok((a_fail.max(b_fail), a.into_chain().into_iter_chain().collect::>())), 202 | }, 203 | Err(a_fail) => match b { 204 | Ok((b_fail, b)) => { 205 | *tokens = b_tokens; 206 | Ok((b_fail.max(a_fail), b.into_chain().into_iter_chain().collect::>())) 207 | }, 208 | Err(b_fail) => Err(a_fail.max(b_fail)), 209 | }, 210 | } 211 | } 212 | } 213 | 214 | // OrChainFallbackFn 215 | 216 | pub struct OrChainFallbackFn(pub A, pub B, pub PhantomData<(O, U)>); 217 | 218 | impl Clone for OrChainFallbackFn { 219 | fn clone(&self) -> Self { 220 | Self(self.0.clone(), self.1.clone(), PhantomData) 221 | } 222 | } 223 | 224 | impl ParseFn, E> for OrChainFallbackFn 225 | where 226 | T: Clone, 227 | O: IntoChain, 228 | U: IntoChain, 229 | E: ParseError, 230 | F: ParseFn, 231 | G: ParseFn, 232 | { 233 | fn parse(&self, tokens: &mut TokenIter) -> ParseResult, E> { 234 | let a = self.0.parse(tokens); 235 | match a { 236 | Ok((a_fail, a)) => Ok((a_fail, a.into_chain().into_iter_chain().collect::>())), 237 | Err(a_fail) => match self.1.parse(tokens) { 238 | Ok((b_fail, b)) => Ok((b_fail.max(a_fail), b.into_chain().into_iter_chain().collect::>())), 239 | Err(b_fail) => Err(a_fail.max(b_fail)), 240 | }, 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /parze-declare/src/rule.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::{ 2 | TokenStream, 3 | TokenTree, 4 | Group, 5 | Delimiter, 6 | quote, 7 | }; 8 | use crate::{ 9 | expr::parse_atom_expr, 10 | TokenIter, 11 | TokenStreamExt, 12 | Error, 13 | attempt, 14 | }; 15 | 16 | fn group_tree(delim: Delimiter, items: TokenStream) -> TokenTree { 17 | TokenTree::Group(Group::new(delim, items)) 18 | } 19 | 20 | fn sym(tt: TokenTree) -> TokenTree { 21 | group_tree(Delimiter::Parenthesis, quote!(parze::prelude::sym).and(group_tree(Delimiter::Parenthesis, tt.into()).into())) 22 | } 23 | 24 | fn link(tt: TokenTree) -> TokenTree { 25 | group_tree(Delimiter::Parenthesis, group_tree(Delimiter::Parenthesis, tt.into()).and(quote!(.link()))) 26 | } 27 | 28 | fn parse_punct<'a>(stream: &mut impl TokenIter, puncts: &[&'a str]) -> Result<&'a str, Error> { 29 | attempt(stream, |stream| { 30 | for punct in puncts { 31 | match attempt(stream, |stream| { 32 | for c in punct.chars() { 33 | match stream.next().ok_or(Error::ExpectedPunct)? { 34 | TokenTree::Punct(punct) if punct.as_char() == c => {}, // TODO: Check joining 35 | _ => return Err(Error::ExpectedPunct), 36 | } 37 | } 38 | Ok(()) 39 | }) { 40 | Ok(()) => return Ok(*punct), 41 | Err(_) => {}, 42 | } 43 | } 44 | Err(Error::ExpectedPunct) 45 | }) 46 | } 47 | 48 | fn parse_atom(stream: &mut impl TokenIter) -> Result { 49 | attempt(stream, |stream| { 50 | let tt = stream.next().ok_or(Error::ExpectedAtom)?; 51 | match &tt { 52 | // Parze expression 53 | TokenTree::Group(group) if group.delimiter() == Delimiter::Parenthesis 54 | => parse_rule(&mut group.stream().into_iter()), 55 | // Embedded Rust expressions 56 | TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => Ok(tt), 57 | TokenTree::Literal(_) => Ok(sym(tt)), 58 | TokenTree::Ident(_) => Ok(link(tt)), 59 | _ => Err(Error::ExpectedAtom), 60 | } 61 | }) 62 | } 63 | 64 | fn parse_unary_suffix( 65 | stream: &mut I, 66 | mut parse_item: impl FnMut(&mut I) -> Result, 67 | mut parse_op: impl FnMut(&mut I) -> Result, 68 | combine: &mut impl FnMut(TokenTree, P) -> TokenTree, 69 | ) -> Result { 70 | attempt(stream, |stream| { 71 | let mut expr = parse_item(stream)?; 72 | 73 | while let Ok(op) = parse_op(stream) { 74 | expr = combine(expr, op); 75 | } 76 | 77 | Ok(expr) 78 | }) 79 | } 80 | 81 | fn parse_repeated(stream: &mut impl TokenIter) -> Result { 82 | parse_unary_suffix( 83 | stream, 84 | parse_atom, 85 | |stream| parse_punct(stream, &["?", "+", "*", "~", ".%", ".#", ".@", ".::"]), 86 | &mut |item, op| group_tree( 87 | Delimiter::Parenthesis, 88 | group_tree(Delimiter::Parenthesis, item.into()) 89 | .and(match op { 90 | "?" => quote!(.or_not()), 91 | "+" => quote!(.repeat(1..)), 92 | "*" => quote!(.repeat(..)), 93 | "~" => quote!(.before_padding()), 94 | ".%" => quote!(.chained()), 95 | ".#" => quote!(.flatten()), 96 | ".@" => quote!(.link()), 97 | ".::" => quote!(.collect()), 98 | _ => unreachable!(), 99 | }) 100 | ), 101 | ) 102 | } 103 | 104 | fn parse_unary_prefix( 105 | stream: &mut I, 106 | mut parse_item: impl FnMut(&mut I) -> Result, 107 | mut parse_op: impl FnMut(&mut I) -> Result, 108 | combine: &mut impl FnMut(P, TokenTree) -> TokenTree, 109 | ) -> Result { 110 | attempt(stream, |stream| { 111 | match parse_op(stream) { 112 | Ok(op) => { 113 | let item = parse_unary_prefix(stream, parse_item, parse_op, combine)?; 114 | Ok(combine(op, item)) 115 | }, 116 | Err(_) => parse_item(stream), 117 | } 118 | }) 119 | } 120 | 121 | fn parse_unary_or(stream: &mut impl TokenIter) -> Result { 122 | parse_unary_prefix( 123 | stream, 124 | parse_repeated, 125 | |stream| parse_punct(stream, &["|", "~"]), 126 | // Ignore '|' in unary prefix position 127 | &mut |op, item| group_tree( 128 | Delimiter::Parenthesis, 129 | group_tree(Delimiter::Parenthesis, item.into()) 130 | .and(match op { 131 | "|" => quote!(), // Ignore '|' in unary prefix position 132 | "~" => quote!(.after_padding()), 133 | _ => unreachable!(), 134 | }) 135 | ), 136 | ) 137 | } 138 | 139 | fn parse_binary( 140 | stream: &mut I, 141 | mut parse_item: impl FnMut(&mut I) -> Result, 142 | mut parse_tail: impl FnMut(&mut I) -> Result, 143 | mut parse_op: impl FnMut(&mut I) -> Result, 144 | mut combine: impl FnMut(TokenTree, P, TokenTree) -> TokenTree, 145 | ) -> Result { 146 | attempt(stream, |stream| { 147 | let mut expr = parse_item(stream)?; 148 | 149 | loop { 150 | let op = match parse_op(stream) { 151 | Ok(op) => op, 152 | Err(_) => break Ok(expr), 153 | }; 154 | 155 | let tail = parse_tail(stream)?; 156 | 157 | expr = combine(expr, op, tail); 158 | } 159 | }) 160 | } 161 | 162 | fn parse_separated(stream: &mut impl TokenIter) -> Result { 163 | parse_binary( 164 | stream, 165 | parse_unary_or, 166 | parse_unary_or, 167 | |stream| parse_punct(stream, &["..."]), 168 | |a, op, b| group_tree( 169 | Delimiter::Parenthesis, 170 | group_tree(Delimiter::Parenthesis, a.into()) 171 | .and(match op { 172 | "..." => quote!(.separated_by), 173 | _ => unreachable!(), 174 | }) 175 | .and(group_tree(Delimiter::Parenthesis, b.into()).into()) 176 | ), 177 | ) 178 | } 179 | 180 | fn parse_then(stream: &mut impl TokenIter) -> Result { 181 | parse_binary( 182 | stream, 183 | parse_separated, 184 | parse_separated, 185 | |stream| parse_punct(stream, &["%", "-&", "&-", "&", "|%"]), 186 | |a, op, b| group_tree( 187 | Delimiter::Parenthesis, 188 | group_tree(Delimiter::Parenthesis, a.into()) 189 | .and(match op { 190 | "%" => quote!(.chain), 191 | "-&" => quote!(.delimiter_for), 192 | "&-" => quote!(.delimited_by), 193 | "&" => quote!(.then), 194 | "|%" => quote!(.or_chain), 195 | _ => unreachable!(), 196 | }) 197 | .and(group_tree(Delimiter::Parenthesis, b.into()).into()) 198 | ), 199 | ) 200 | } 201 | 202 | fn parse_mapping(stream: &mut impl TokenIter) -> Result { 203 | parse_binary( 204 | stream, 205 | parse_then, 206 | parse_atom_expr, 207 | |stream| parse_punct(stream, &["=>", "->", "<:", ":>"]), 208 | |a, op, b| group_tree( 209 | Delimiter::Parenthesis, 210 | group_tree(Delimiter::Parenthesis, a.into()) 211 | .and(match op { 212 | "=>" => quote!(.map), 213 | "->" => quote!(.to), 214 | "<:" => quote!(.reduce_left), 215 | ":>" => quote!(.reduce_right), 216 | _ => unreachable!(), 217 | }) 218 | .and(group_tree(Delimiter::Parenthesis, b.into()).into()) 219 | ), 220 | ) 221 | } 222 | 223 | fn parse_or(stream: &mut impl TokenIter) -> Result { 224 | parse_binary( 225 | stream, 226 | parse_mapping, 227 | parse_mapping, 228 | |tokens| parse_punct(tokens, &["|"]), 229 | |a, _, b| group_tree( 230 | Delimiter::Parenthesis, 231 | group_tree(Delimiter::Parenthesis, a.into()) 232 | .and(quote!(.or)) 233 | .and(group_tree(Delimiter::Parenthesis, b.into()).into()) 234 | ), 235 | ) 236 | } 237 | 238 | pub(crate) fn parse_rule(stream: &mut impl TokenIter) -> Result { 239 | let result = parse_or(stream)?; 240 | match stream.next() { 241 | Some(_) => Err(Error::UnexpectedToken), 242 | None => Ok(result), 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Parze is a clean, efficient parser combinator written in Rust. 2 | //! 3 | //! # Features 4 | //! 5 | //! - All the usual parser combinator operations 6 | //! - Macro for simple rule and parser declaration 7 | //! - Support for recursive parser definitions 8 | //! - Custom error types - define your own! 9 | //! - Prioritised / merged failure for more useful errors 10 | //! - No dependencies - fast compilation! 11 | //! - `no_std` support 12 | //! 13 | //! # Example 14 | //! 15 | //! A parser capable of parsing all valid Brainfuck code into an AST. 16 | //! 17 | //! ``` 18 | //! #![cfg(feature = "macros")] 19 | //! #![feature(proc_macro_hygiene)] 20 | //! 21 | //! use parze::prelude::*; 22 | //! 23 | //! #[derive(Clone, Debug, PartialEq)] 24 | //! enum Instr { Add, Sub, Left, Right, In, Out, Loop(Vec) } 25 | //! 26 | //! parsers! { 27 | //! bf: Parser = { 28 | //! ( '+' -> { Instr::Add } 29 | //! | '-' -> { Instr::Sub } 30 | //! | '<' -> { Instr::Left } 31 | //! | '>' -> { Instr::Right } 32 | //! | ',' -> { Instr::In } 33 | //! | '.' -> { Instr::Out } 34 | //! | '[' -& bf &- ']' => { |i| Instr::Loop(i) } 35 | //! ) * 36 | //! } 37 | //! } 38 | //! ``` 39 | 40 | extern crate alloc; 41 | 42 | pub mod error; 43 | pub mod repeat; 44 | pub mod reduce; 45 | pub mod pad; 46 | pub mod chain; 47 | pub mod parse_fn; 48 | mod fail; 49 | 50 | // Reexports 51 | 52 | /// A macro to define parser rules. 53 | /// 54 | /// # Operators 55 | /// 56 | /// Listed in order of precedence 57 | /// 58 | /// | Syntax | Name | Description | 59 | /// |------------|----------------|---------------------------------------| 60 | /// | ~ x | after padding | Equivalent to `x.after_padding()` | 61 | /// | x ~ | before padding | Equivalent to `x.before_padding()` | 62 | /// | x * | any | Equivalent to `x.repeat(..)` | 63 | /// | x + | at least one | Equivalent to `x.repeat(1..)` | 64 | /// | x ? | optional | Equivalent to `x.or_not()` | 65 | /// | x .% | chained | Equivalent to `x.chained()` | 66 | /// | x .# | flatten | Equivalent to `x.flatten()` | 67 | /// | x .@ | link | Equivalent to `x.link()` | 68 | /// | x ... y | separated | Equivalent to `x.separated_by(y)` | 69 | /// | x & y | then | Equivalent to `x.then(y)` | 70 | /// | x % y | chain | Equivalent to `x.chain(y)` | 71 | /// | x \|% y | or chain | Equivalent to `x.or_chain(y)` | 72 | /// | x -& y | delimiter for | Equivalent to `x.delimiter_for(y)` | 73 | /// | x &- y | delimited by | Equivalent to `x.delimited_by(y)` | 74 | /// | x -> Y | to | Equivalent to `x.to(y)` | 75 | /// | x => F | map | Equivalent to `x.map(F)` | 76 | /// | x <: F | reduce left | Equivalent to `x.reduce_left(F)` | 77 | /// | x :> F | reduce right | Equivalent to `x.reduce_right(F)` | 78 | /// | x \| y | or | Equivalent to `x.or(y)` | 79 | /// | { X } | expr | Considers `X` to be a Rust expression | 80 | pub use parze_declare::rule; 81 | 82 | use alloc::rc::Rc; 83 | use core::{ 84 | slice, 85 | iter::{self, FromIterator}, 86 | cell::RefCell, 87 | borrow::Borrow, 88 | marker::PhantomData, 89 | }; 90 | use crate::{ 91 | error::{ParseError, DefaultParseError}, 92 | repeat::Repeat, 93 | fail::{Fail, MayFail}, 94 | reduce::{ReduceLeft, ReduceRight}, 95 | pad::Padded, 96 | chain::{Chain, IntoChain, Single}, 97 | parse_fn::{ 98 | ParseFn, RcParseFn, GenParseFn, 99 | ThenFn, ChainFn, 100 | OrFn, OrFallbackFn, OrChainFn, OrChainFallbackFn, 101 | }, 102 | }; 103 | 104 | type TokenIter<'a, T> = iter::Enumerate>>; 105 | 106 | type ParseResult = Result<(MayFail, O), Fail>; 107 | 108 | /// A type that represents a rule that may be used to parse a list of symbols. 109 | /// 110 | /// Parsers may be combined and manipulated in various ways to create new parsers. 111 | pub struct Parser<'a, T, O = T, E = DefaultParseError, F = RcParseFn<'a, T, O, E>> 112 | where 113 | T: Clone + 'a, 114 | O: 'a, 115 | E: ParseError + 'a, 116 | F: ParseFn + 'a, 117 | { 118 | f: F, 119 | _phantom: PhantomData<&'a (T, O, E)>, 120 | } 121 | 122 | impl<'a, T: Clone + 'a, O: 'a, E: ParseError + 'a, F: ParseFn + 'a> Clone for Parser<'a, T, O, E, F> { 123 | fn clone(&self) -> Self { 124 | Self { 125 | f: self.f.clone(), 126 | _phantom: PhantomData, 127 | } 128 | } 129 | } 130 | 131 | impl<'a, T: Clone + 'a, O: 'a, E: ParseError + 'a> Parser<'a, T, O, E> { 132 | fn dynamic_raw(f: impl Fn(&mut TokenIter) -> ParseResult + 'a) -> Self { 133 | Parser { 134 | f: RcParseFn::new(f), 135 | _phantom: PhantomData, 136 | } 137 | } 138 | 139 | fn dynamic(f: impl Fn(&mut TokenIter) -> ParseResult + 'a) -> Self { 140 | Self::dynamic_raw(move |tokens| attempt(tokens, &f)) 141 | } 142 | 143 | fn generic_raw<'b>(f: impl Fn(&mut TokenIter) -> ParseResult + Clone + 'a) -> Parser<'b, T, O, E, impl ParseFn + Captures<'b> + 'a> 144 | where 'a: 'b, 'b: 'a 145 | { 146 | Parser { 147 | f: GenParseFn::new(f), 148 | _phantom: PhantomData, 149 | } 150 | } 151 | 152 | fn generic<'b>(f: impl Fn(&mut TokenIter) -> ParseResult + Clone + 'a) -> Parser<'b, T, O, E, impl ParseFn + Captures<'b> + 'a> 153 | where 'a: 'b, 'b: 'a 154 | { 155 | // TODO: Switch to `Self::generic_raw` 156 | Self::dynamic_raw(move |tokens| attempt(tokens, &f)) 157 | } 158 | 159 | fn try_map<'b>(f: impl Fn(T) -> Result + Clone + 'a) -> Parser<'b, T, O, E, impl ParseFn + Captures<'b> + 'a> 160 | where 'a: 'b, 'b: 'a 161 | { 162 | Self::generic_raw(move |tokens| try_parse(tokens, |(idx, tok), _| { 163 | match f(tok.clone()) { 164 | Ok(output) => Ok((MayFail::none(), output)), 165 | Err(err) => Err(Fail::new(idx, err)), 166 | } 167 | })) 168 | } 169 | } 170 | 171 | pub trait Captures<'a> {} 172 | impl<'a, T: ?Sized> Captures<'a> for T {} 173 | 174 | impl<'a, T: Clone + 'a, O: 'a, E: ParseError + 'a, F: ParseFn + 'a> Parser<'a, T, O, E, F> { 175 | fn new(f: F) -> Self { 176 | Self { 177 | f, 178 | _phantom: PhantomData, 179 | } 180 | } 181 | 182 | fn call(f: impl Fn() -> Self + 'a) -> Parser<'a, T, O, E> { 183 | Parser::dynamic(move |tokens| f().f.parse(tokens)) 184 | } 185 | 186 | /// Convert this parser into one with a standard payload that may be accepted by more functions 187 | pub fn to_object(&self) -> Parser<'a, T, O, E> { 188 | let this = self.clone(); 189 | Parser::dynamic_raw(move |tokens| this.f.parse(tokens)) 190 | } 191 | 192 | /// Undocumented 193 | pub fn link(&self) -> Self { 194 | self.clone() 195 | } 196 | 197 | // Combinator methods 198 | 199 | /// Map the output of this parser to another value with the given function. 200 | pub fn map<'b, U: 'a>(self, f: impl Fn(O) -> U + Clone + 'a) -> Parser<'a, T, U, E, impl ParseFn + Captures<'b> + 'a> 201 | where 'a: 'b, 'b: 'a 202 | { 203 | Parser::generic_raw(move |tokens| self.f.parse(tokens).map(|(e, o)| (e, f(o)))) 204 | } 205 | 206 | /// Map the error of this parser to another with the given function. 207 | /// 208 | /// This may be used to annotate an error with contextual information at some stage of parsing. 209 | pub fn map_err<'b, G: ParseError + 'a>(self, f: impl Fn(E) -> G + Clone + 'a) -> Parser<'a, T, O, G, impl ParseFn + Captures<'b> + 'a> 210 | where 'a: 'b, 'b: 'a 211 | { 212 | Parser::generic_raw(move |tokens| match self.f.parse(tokens) { 213 | Ok((fail, output)) => Ok((fail.map_err(&f), output)), 214 | Err(fail) => Err(fail.map_err(&f)), 215 | }) 216 | } 217 | 218 | /// Discard the output of this parser (i.e: create a parser that outputs `()` instead). 219 | pub fn discard<'b>(self) -> Parser<'a, T, (), E, impl ParseFn + Captures<'b> + 'a> 220 | where 'a: 'b, 'b: 'a 221 | { 222 | self.map(|_| ()) 223 | } 224 | 225 | /// Map all outputs of this parser to the given value. 226 | pub fn to<'b, U: Clone + 'a>(self, to: U) -> Parser<'a, T, U, E, impl ParseFn + Captures<'b> + 'a> 227 | where 'a: 'b, 'b: 'a 228 | { 229 | self.map(move |_| to.clone()) 230 | } 231 | 232 | /// Create a parser that optionally parses symbols that match this parser. 233 | pub fn or_not<'b>(self) -> Parser<'a, T, Option, E, impl ParseFn, E> + Captures<'b> + 'a> 234 | where 'a: 'b, 'b: 'a 235 | { 236 | Parser::generic_raw(move |tokens| { 237 | match self.f.parse(tokens) { 238 | Ok((fail, output)) => Ok((fail, Some(output))), 239 | Err(fail) => Ok((fail.into(), None)), 240 | } 241 | }) 242 | } 243 | 244 | /// Create a parser that parses symbols that match this parser and then another parser. 245 | pub fn then<'b, U: 'b, G: ParseFn + 'b>(self, other: Parser<'a, T, U, E, G>) -> Parser<'a, T, (O, U), E, impl ParseFn + Captures<'b> + 'a> 246 | where 'a: 'b, 'b: 'a 247 | { 248 | Parser::new(ThenFn(self.f, other.f)) 249 | } 250 | 251 | /// Create a parser that parsers the same symbols as this parser, but emits its output as a chain. 252 | /// 253 | /// This is most useful when you wish to use singular outputs as part of a chain. 254 | pub fn chained<'b>(self) -> Parser<'a, T, Single, E, impl ParseFn, E> + Captures<'b> + 'a> 255 | where 'a: 'b, 'b: 'a 256 | { 257 | self.map(|output| Single::from(output)) 258 | } 259 | 260 | /// Create a parser that parses symbols that match this parser and then another parser. 261 | /// 262 | /// Unlike `.then`, this method will chain the two parser outputs together as a chain. 263 | pub fn chain<'b, U: 'a, V: 'a, G: ParseFn + 'a>(self, other: Parser<'a, T, U, E, G>) -> Parser<'a, T, Vec, E, impl ParseFn, E> + Captures<'b> + 'a> 264 | where O: IntoChain, U: IntoChain, 'a: 'b, 'b: 'a 265 | { 266 | Parser::new(ChainFn(self.f, other.f, PhantomData)) 267 | } 268 | 269 | /// Create a parser that with a flatter output than this parser. 270 | pub fn flatten<'b, U: 'a, V: 'a>(self) -> Parser<'a, T, Vec, E, impl ParseFn, E> + Captures<'b> + 'a> 271 | where O: IntoChain, U: IntoChain, 'a: 'b, 'b: 'a 272 | { 273 | self.map(|a| a.into_chain().into_iter_chain().map(|a| a.into_chain().into_iter_chain()).flatten().collect::>()) 274 | } 275 | 276 | /// Create a parser that parses symbols that match this parser and then another parser, discarding the output of this parser. 277 | pub fn delimiter_for<'b, U: 'a>(self, other: Parser<'a, T, U, E, impl ParseFn>) -> Parser<'a, T, U, E, impl ParseFn + Captures<'b> + 'a> 278 | where 'a: 'b, 'b: 'a 279 | { 280 | self.then(other).map(|(_, b)| b) 281 | } 282 | 283 | /// Create a parser that parses symbols that match this parser and then another parser, discarding the output of the other parser. 284 | pub fn delimited_by<'b, U: 'a>(self, other: Parser<'a, T, U, E, impl ParseFn>) -> Parser<'a, T, O, E, impl ParseFn + Captures<'b> + 'a> 285 | where 'a: 'b, 'b: 'a 286 | { 287 | self.then(other).map(|(a, _)| a) 288 | } 289 | 290 | /// Create a parser that accepts the valid input of this parser separated by the valid input of another. 291 | pub fn separated_by<'b, U: 'a>(self, other: Parser<'a, T, U, E, impl ParseFn>) -> Parser<'a, T, Vec, E, impl ParseFn, E> + Captures<'b> + 'a> 292 | where 'a: 'b, 'b: 'a 293 | { 294 | Parser::generic(move |tokens| { 295 | let mut max_err = MayFail::none(); 296 | let mut outputs = Vec::new(); 297 | 298 | let mut old_tokens = tokens.clone(); 299 | 300 | for i in 0.. { 301 | match self.f.parse(tokens) { 302 | Ok((err, output)) => { 303 | max_err = max_err.max(err); 304 | outputs.push(output); 305 | }, 306 | Err(err) => { 307 | max_err = max_err.max(err); 308 | *tokens = old_tokens; 309 | break; 310 | }, 311 | } 312 | 313 | old_tokens = tokens.clone(); 314 | 315 | match other.f.parse(tokens) { 316 | Ok((err, _)) => max_err = max_err.max(err), 317 | Err(err) => { 318 | max_err = max_err.max(err); 319 | break; 320 | }, 321 | } 322 | } 323 | 324 | Ok((max_err, outputs)) 325 | }) 326 | } 327 | 328 | /// Create a parser that parses character-like symbols that match this parser followed or preceded by any number of 'padding' (usually taken to mean whitespace) symbols. 329 | /// 330 | /// You can implement the `Padded` trait for your own symbol types to use this method with them. 331 | pub fn padded<'b, U: Padded>(self) -> Parser<'a, T, O, E, impl ParseFn + Captures<'b> + 'a> 332 | where T: Borrow, 'a: 'b, 'b: 'a 333 | { 334 | self.after_padding().before_padding() 335 | } 336 | 337 | /// Create a parser that parses any symbols that match this parser followed by any number of 'padding' (usually taken to mean whitespace) symbols. 338 | /// 339 | /// You can implement the `Padded` trait for your own symbol types to use this method with them. 340 | pub fn before_padding<'b, U: Padded>(self) -> Parser<'a, T, O, E, impl ParseFn + Captures<'b> + 'a> 341 | where T: Borrow, 'a: 'b, 'b: 'a 342 | { 343 | self.delimited_by(padding()) 344 | } 345 | 346 | /// Create a parser that parses any number of 'padding' (usually taken to mean whitespace) symbols followed by any symbols that match this parser. 347 | /// 348 | /// You can implement the `Padded` trait for your own symbol types to use this method with them. 349 | pub fn after_padding<'b, U: Padded>(self) -> Parser<'a, T, O, E, impl ParseFn + Captures<'b> + 'a> 350 | where T: Borrow, 'a: 'b, 'b: 'a 351 | { 352 | padding().delimiter_for(self) 353 | } 354 | 355 | /// Create a parser that parses symbols that match this parser or another parser. 356 | pub fn or<'b>(self, other: Parser<'b, T, O, E, impl ParseFn>) -> Parser<'b, T, O, E, impl ParseFn + Captures<'b> + 'a> 357 | where 'a: 'b, 'b: 'a 358 | { 359 | Parser::new(OrFn(self.f, other.f)) 360 | } 361 | 362 | /// Create a parser that parses symbols that match this parser or another parser where both parsers are chains. 363 | pub fn or_chain>(self, other: Parser<'a, T, U, E, G>) -> Parser<'a, T, Vec, E, OrChainFn> 364 | where O: IntoChain, U: IntoChain 365 | { 366 | Parser::new(OrChainFn(self.f, other.f, PhantomData)) 367 | } 368 | 369 | /// Create a parser that parses symbols that match this parser or another fallback parser where both parsers are chains, prioritising errors from this parser. 370 | /// 371 | /// This method is 'short-circuiting': if this parser succeeds, the fallback parser won't even be invoked. 372 | /// This means that emitted errors may not include information from the fallback parser. 373 | pub fn or_chain_fallback>(self, other: Parser<'a, T, U, E, G>) -> Parser<'a, T, Vec, E, OrChainFallbackFn> 374 | where O: IntoChain, U: IntoChain 375 | { 376 | Parser::new(OrChainFallbackFn(self.f, other.f, PhantomData)) 377 | } 378 | 379 | /// Create a parser that parses symbols that match this parser or another fallback parser, prioritising errors from this parser. 380 | /// 381 | /// This method is 'short-circuiting': if this parser succeeds, the fallback parser won't even be invoked. 382 | /// This means that emitted errors may not include information from the fallback parser. 383 | pub fn or_fallback<'b, G: ParseFn>(self, other: Parser<'a, T, O, E, G>) -> Parser<'b, T, O, E, impl ParseFn + Captures<'b> + 'a> 384 | where 'a: 'b, 'b: 'a 385 | { 386 | Parser::new(OrFallbackFn(self.f, other.f)) 387 | } 388 | 389 | /// Create a parser that parses symbols that match this parser multiple times, according to the given repetition rule. 390 | pub fn repeat<'b>(self, repeat: impl Into) -> Parser<'a, T, Vec, E, impl ParseFn, E> + Captures<'b> + 'a> 391 | where 'a: 'b, 'b: 'a 392 | { 393 | let repeat = repeat.into(); 394 | Parser::generic(move |tokens| { 395 | let mut max_err = MayFail::none(); 396 | let mut outputs = Vec::new(); 397 | 398 | for i in 0..repeat.idx_upper_limit() { 399 | match self.f.parse(tokens) { 400 | Ok((err, output)) => { 401 | max_err = max_err.max(err); 402 | outputs.push(output); 403 | }, 404 | Err(err) if repeat.contains(i) => { 405 | max_err = max_err.max(err); 406 | break; 407 | }, 408 | Err(err) => return Err(err.max(max_err)), 409 | } 410 | } 411 | 412 | Ok((max_err, outputs)) 413 | }) 414 | } 415 | 416 | /// Create a parser that parses symbols that match this parser and then another parser. 417 | pub fn collect<'b, U: FromIterator>(self) -> Parser<'a, T, U, E, impl ParseFn + Captures<'b> + 'a> 418 | where O: IntoIterator, 'a: 'b, 'b: 'a 419 | { 420 | self.map(|output| output.into_iter().collect()) 421 | } 422 | 423 | /// Create a parser that left-reduces this parser down into another type according to the given reduction function. 424 | pub fn reduce_left<'b, A, B>(self, f: impl Fn(B, A) -> A + Clone + 'a) -> Parser<'a, T, A, E, impl ParseFn + Captures<'b> + 'a> 425 | where O: ReduceLeft, 'a: 'b, 'b: 'a 426 | { 427 | self.map(move |output| output.reduce_left(&f)) 428 | } 429 | 430 | /// Create a parser that right-reduces this parser down into another type according to the given reduction function. 431 | pub fn reduce_right<'b, A, B>(self, f: impl Fn(A, B) -> A + Clone + 'a) -> Parser<'a, T, A, E, impl ParseFn + Captures<'b> + 'a> 432 | where O: ReduceRight, 'a: 'b, 'b: 'a 433 | { 434 | self.map(move |output| output.reduce_right(&f)) 435 | } 436 | 437 | /// Attempt to parse an array-like list of symbols using this parser. 438 | pub fn parse(&self, tokens: &[T]) -> Result { 439 | let mut tokens = tokens.iter().cloned().enumerate(); 440 | let (fail, output) = self.f.parse(&mut tokens).map_err(|e| e.take_error())?; 441 | if let Some((idx, tok)) = tokens.next() { 442 | Err(Fail::new(idx, E::unexpected(tok)).max(fail).take_error()) 443 | } else { 444 | Ok(output) 445 | } 446 | } 447 | } 448 | 449 | impl<'a, O: 'a, E: ParseError + 'a, F: ParseFn + 'a> Parser<'a, char, O, E, F> { 450 | /// Attempt to parse a string using this parser. 451 | pub fn parse_str(&self, string: impl AsRef) -> Result { 452 | // I'd like to find a way around doing this... alas, I can't think of one other than boxing the iterator. 453 | self.parse(&string.as_ref().chars().collect::>()) 454 | } 455 | } 456 | 457 | // Declaration 458 | 459 | /// A type used to separate the declaration and definition of a parser such that it may be defined in terms of itself. 460 | /// 461 | /// This type is the primary route through which recursive parsers are defined, although `call(f)` may also be used. 462 | pub struct Declaration<'a, T: Clone + 'a, O: 'a, E: ParseError = DefaultParseError> { 463 | parser: Rc>>>, 464 | } 465 | 466 | impl<'a, T: Clone + 'a, O: 'a, E: ParseError> Default for Declaration<'a, T, O, E> { 467 | fn default() -> Self { 468 | Self { parser: Rc::new(RefCell::new(None)) } 469 | } 470 | } 471 | 472 | impl<'a, T: Clone + 'a, O: 'a, E: ParseError + 'a> Declaration<'a, T, O, E> { 473 | /// Create a parser that is linked to this declaration. 474 | /// If the resultant parser is used before this declaration is defined (see `.define`) then a panic will occur. 475 | pub fn link(&self) -> Parser<'a, T, O, E> { 476 | let parser = Rc::downgrade(&self.parser); 477 | Parser::dynamic(move |tokens| { 478 | (*parser.upgrade().expect("Parser was dropped")).borrow().as_ref().expect("Parser was declared but not defined").f.parse(tokens) 479 | }) 480 | } 481 | 482 | /// Provider a parser definition for this declaration, thereby sealing it as a well-defined parser. 483 | pub fn define(self, parser: Parser<'a, T, O, E, impl ParseFn>) -> Parser<'a, T, O, E> { 484 | *self.parser.borrow_mut() = Some(parser.to_object()); 485 | Parser::dynamic(move |tokens| (*self.parser).borrow().as_ref().unwrap().f.parse(tokens)) 486 | } 487 | } 488 | 489 | /// Declare a parser before defining it. A definition can be given later with the `.define` method. 490 | /// 491 | /// This function is generally used to create recursive parsers, along with `call`. 492 | pub fn declare<'a, T: Clone + 'a, O: 'a, E: ParseError + 'a>() -> Declaration<'a, T, O, E> { 493 | Declaration::default() 494 | } 495 | 496 | /// A wrapper function for recursive parser declarations. 497 | /// 498 | /// This function uses `Declaration` internally. 499 | pub fn recursive<'a, T: Clone + 'a, O: 'a, E: ParseError + 'a>(f: impl FnOnce(Parser<'a, T, O, E>) -> Parser<'a, T, O, E> + 'a) -> Parser<'a, T, O, E> { 500 | let p = Declaration::default(); 501 | let p_link = p.link(); 502 | p.define(f(p_link)) 503 | } 504 | 505 | // Helpers 506 | 507 | fn attempt<'a, T: Clone + 'a, R, F, E: ParseError + 'a>(tokens: &mut TokenIter, f: F) -> Result> 508 | where F: FnOnce(&mut TokenIter) -> Result>, 509 | { 510 | let mut tokens2 = tokens.clone(); 511 | let tok = f(&mut tokens2)?; 512 | *tokens = tokens2; 513 | Ok(tok) 514 | } 515 | 516 | fn try_parse<'a, T: Clone + 'a, R, F, E: ParseError + 'a>(tokens: &mut TokenIter, f: F) -> Result<(MayFail, R), Fail> 517 | where F: FnOnce((usize, T), &mut TokenIter) -> Result<(MayFail, R), Fail>, 518 | { 519 | attempt(tokens, |tokens| f(tokens.next().ok_or_else(|| Fail::new(!0, E::unexpected_end()))?, tokens)) 520 | } 521 | 522 | // Utility 523 | 524 | /// A parser that accepts the given symbol. 525 | pub fn sym<'b, 'a, T: Clone + 'a, E: ParseError + 'a, U: Clone + 'a>(expected: U) -> Parser<'a, T, T, E, impl ParseFn + Captures<'b> + 'a> 526 | where T: PartialEq, 'a: 'b, 'b: 'a 527 | { 528 | permit(move |tok: T| tok == expected) 529 | } 530 | 531 | /// A parser that accepts any one thing that is not the given symbol. 532 | pub fn not_sym<'b, 'a, T: Clone + 'a + PartialEq, E: ParseError + 'a, U: Borrow + Clone + 'a>(expected: U) -> Parser<'a, T, T, E, impl ParseFn + Captures<'b> + 'a> 533 | where 'a: 'b, 'b: 'a 534 | { 535 | permit(move |tok: T| &tok != expected.borrow()) 536 | } 537 | 538 | /// A parser that accepts one of the given set of symbols. 539 | pub fn one_of<'b, 'a, T: Clone + 'a + PartialEq, E: ParseError + 'a, U: Borrow + Clone + 'a>(expected: impl IntoIterator) -> Parser<'a, T, T, E, impl ParseFn + Captures<'b> + 'a> 540 | where 'a: 'b, 'b: 'a 541 | { 542 | let expected = expected.into_iter().collect::>(); 543 | permit(move |tok| expected.iter().any(|e| &tok == e.borrow())) 544 | } 545 | 546 | /// A parser that accepts any one symbol that is not within the given set of symbols. 547 | pub fn none_of<'b, 'a, T: Clone + 'a + PartialEq, E: ParseError + 'a, U: Borrow + Clone + 'a>(unexpected: impl IntoIterator) -> Parser<'a, T, T, E, impl ParseFn + Captures<'b> + 'a> 548 | where 'a: 'b, 'b: 'a 549 | { 550 | let unexpected = unexpected.into_iter().collect::>(); 551 | permit(move |tok| unexpected.iter().all(|e| &tok != e.borrow())) 552 | } 553 | 554 | /// A parser that accepts all of the given list of symbols, one after another. 555 | pub fn all_of<'b, 'a, T: Clone + 'a + PartialEq, E: ParseError + 'a, U: Borrow + Clone + 'a>(expected: impl IntoIterator) -> Parser<'a, T, Vec, E, impl ParseFn, E> + Captures<'b> + 'a> 556 | where 'a: 'b, 'b: 'a 557 | { 558 | let expected = expected.into_iter().collect::>(); 559 | Parser::generic(move |tokens| { 560 | let mut outputs = Vec::new(); 561 | for expected in &expected { 562 | match tokens.next() { 563 | Some((_, output)) if &output == expected.borrow() => outputs.push(output), 564 | Some((idx, output)) => return Err(Fail::new(idx, E::unexpected(output))), 565 | None => return Err(Fail::new(!0, E::unexpected_end())), 566 | } 567 | } 568 | Ok((MayFail::none(), outputs)) 569 | }) 570 | } 571 | 572 | /// A parser that accepts one symbol provided it passes the given test. 573 | pub fn permit<'b, 'a, T: Clone + 'a, E: ParseError + 'a>(f: impl Fn(T) -> bool + Clone + 'a) -> Parser<'a, T, T, E, impl ParseFn + Captures<'b> + 'a> 574 | where 'a: 'b, 'b: 'a 575 | { 576 | Parser::try_map(move |tok: T| if f(tok.clone()) { Ok(tok) } else { Err(E::unexpected(tok)) }) 577 | } 578 | 579 | /// A parser that accepts one symbol provided it passes the given test, mapping it to another symbol in the process. 580 | /// 581 | /// This function is extremely powerful, and is actually a superset of several other parser functions defined in this crate. 582 | /// However, this power also makes it fairly awkward to use. You might be better served by another function. 583 | pub fn try_map<'b, 'a, T: Clone + 'a, O: 'a, E: ParseError + 'a>(f: impl Fn(T) -> Result + Clone + 'a) -> Parser<'a, T, O, E, impl ParseFn + Captures<'b> + 'a> 584 | where 'a: 'b, 'b: 'a 585 | { 586 | Parser::try_map(f) 587 | } 588 | 589 | /// A parser that accepts one symbol provided it passes the given test, mapping it to another symbol in the process. 590 | pub fn maybe_map<'b, 'a, T: Clone + 'a, O: 'a, E: ParseError + 'a>(f: impl Fn(T) -> Option + Clone + 'a) -> Parser<'a, T, O, E, impl ParseFn + Captures<'b> + 'a> 591 | where 'a: 'b, 'b: 'a 592 | { 593 | Parser::try_map(move |tok: T| f(tok.clone()).ok_or(E::unexpected(tok))) 594 | } 595 | 596 | /// A parser that accepts any single symbol. 597 | pub fn any_sym<'b, 'a, T: Clone + 'a, E: ParseError + 'a>() -> Parser<'a, T, T, E, impl ParseFn + Captures<'b> + 'a> 598 | where 'a: 'b, 'b: 'a 599 | { 600 | permit(|_| true) 601 | } 602 | 603 | /// A parser that accepts all input symbols. 604 | pub fn all_sym<'b, 'a, T: Clone + 'a + PartialEq, E: ParseError + 'a>() -> Parser<'a, T, Vec, E, impl ParseFn, E> + Captures<'b> + 'a> 605 | where 'a: 'b, 'b: 'a 606 | { 607 | permit(|_| true).repeat(..) 608 | } 609 | 610 | /// A parser that does not accept any input symbols. 611 | pub fn nothing<'b, 'a, T: Clone + 'a, E: ParseError + 'a>() -> Parser<'a, T, (), E, impl ParseFn + Captures<'b> + 'a> 612 | where 'a: 'b, 'b: 'a 613 | { 614 | permit(|_| false).discard() 615 | } 616 | 617 | /// A parser that accepts no symbols. 618 | pub fn empty<'b, 'a, T: Clone + 'a + PartialEq, E: ParseError + 'a>() -> Parser<'a, T, (), E, impl ParseFn + Captures<'b> + 'a> 619 | where 'a: 'b, 'b: 'a 620 | { 621 | Parser::generic(|_| Ok((MayFail::none(), ()))) 622 | } 623 | 624 | /// A parser that accepts any number of 'padding' symbols. Usually, this is taken to mean whitespace. 625 | /// 626 | /// You can implement the `Padded` trait for your own symbol types to use this function with them. 627 | pub fn padding<'b, 'a, T: Clone + 'a, U: Padded, E: ParseError + 'a>() -> Parser<'a, T, (), E, impl ParseFn + Captures<'b> + 'a> 628 | where T: Borrow, 'a: 'b, 'b: 'a 629 | { 630 | Parser::generic_raw(|tokens: &mut TokenIter| { 631 | while tokens 632 | .clone() 633 | .next() 634 | .map(|(_, token)| token.borrow().is_padding()) 635 | .unwrap_or(false) 636 | { 637 | tokens.next(); 638 | } 639 | 640 | Ok((MayFail::none(), ())) 641 | }) 642 | } 643 | 644 | /// A parser that invokes another parser, as generated by the given function. 645 | /// 646 | /// This function is generally used to create recursive parsers, along with `Declaration`. 647 | pub fn call<'a, T: Clone + 'a, O: 'a, E: ParseError + 'a>(f: impl Fn() -> Parser<'a, T, O, E> + 'a) -> Parser<'a, T, O, E> { 648 | Parser::call(f) 649 | } 650 | 651 | pub mod prelude { 652 | pub use super::{ 653 | error::{self, ParseError, DefaultParseError}, 654 | repeat::{self, Repeat, Repeat::Any}, 655 | chain::{Chain, IntoChain}, 656 | Parser, 657 | Declaration, 658 | declare, 659 | recursive, 660 | sym, 661 | not_sym, 662 | one_of, 663 | none_of, 664 | all_of, 665 | nothing, 666 | empty, 667 | permit, 668 | padding, 669 | maybe_map, 670 | try_map, 671 | call, 672 | rule, 673 | parsers, 674 | }; 675 | } 676 | 677 | #[cfg(feature = "macros")] 678 | #[macro_export] 679 | macro_rules! parze_error { 680 | () => {}; 681 | } 682 | 683 | /// A macro to define recursive parsers. 684 | /// 685 | /// Parsers defined by this macro may be arbitrarily self-referential, unlike `rule!`. 686 | /// 687 | /// # Example 688 | /// 689 | /// ``` 690 | /// #![cfg(feature = "macros")] 691 | /// #![feature(proc_macro_hygiene)] 692 | /// 693 | /// use parze::prelude::*; 694 | /// 695 | /// parsers! { 696 | /// foo = { 697 | /// '!' | bar 698 | /// } 699 | 700 | /// bar: Parser = { 701 | /// '(' -& foo &- ')' 702 | /// } 703 | /// } 704 | /// ``` 705 | #[cfg(feature = "macros")] 706 | #[macro_export] 707 | macro_rules! parsers { 708 | ( @NAMES ) => {}; 709 | ( @NAMES $name:ident : $kind:ty = { $($rule:tt)* } $($tail:tt)* ) => { 710 | let $name = $crate::declare(); 711 | $crate::parsers!(@NAMES $($tail)*); 712 | }; 713 | ( @NAMES $name:ident = { $($rule:tt)* } $($tail:tt)* ) => { 714 | $crate::parsers!(@NAMES $name : _ = { $($rule)* } $($tail)*); 715 | }; 716 | ( @NAMES $($tail:tt)* ) => { 717 | $crate::parze_error!($($tail)*); 718 | }; 719 | 720 | ( @DEFINITIONS ) => {}; 721 | ( @DEFINITIONS $name:ident : $kind:ty = { $($rule:tt)* } $($tail:tt)* ) => { 722 | let __tmp = $crate::rule!($($rule)*); 723 | let $name : $kind = ($name).define(__tmp); 724 | $crate::parsers!(@DEFINITIONS $($tail)*); 725 | }; 726 | ( @DEFINITIONS $name:ident = { $($rule:tt)* } $($tail:tt)* ) => { 727 | $crate::parsers!(@DEFINITIONS $name : _ = { $($rule)* } $($tail)*); 728 | }; 729 | ( @DEFINITIONS $($tail:tt)* ) => { 730 | $crate::parze_error!($($tail)*); 731 | }; 732 | 733 | ( $($tail:tt)* ) => { 734 | $crate::parsers!(@NAMES $($tail)*); 735 | $crate::parsers!(@DEFINITIONS $($tail)*); 736 | }; 737 | } 738 | 739 | 740 | -------------------------------------------------------------------------------- /benches/sample.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": "5dddbc7c616390a89dc9ee0f", 4 | "index": 0, 5 | "guid": "2e21032e-dc34-4415-aee1-2eff0de8211e", 6 | "isActive": false, 7 | "balance": "$2,559.43", 8 | "picture": "http://placehold.it/32x32", 9 | "age": 35, 10 | "eyeColor": "green", 11 | "name": "Larson Duncan", 12 | "gender": "male", 13 | "company": "SONIQUE", 14 | "email": "larsonduncan@sonique.com", 15 | "phone": "+1 (868) 540-2250", 16 | "address": "271 Banker Street, Laurelton, West Virginia, 4393", 17 | "about": "Duis adipisicing do fugiat dolore dolor dolor magna irure dolor aliquip. Consequat aute qui cillum in nisi magna incididunt duis. Non ad mollit qui nisi proident aliquip nostrud aliquip. Et officia minim anim tempor nulla minim adipisicing magna sint reprehenderit deserunt laboris.\r\n", 18 | "registered": "2015-05-17T11:00:07 -01:00", 19 | "latitude": -5.295299, 20 | "longitude": -30.012684, 21 | "tags": [ 22 | "Lorem", 23 | "tempor", 24 | "nulla", 25 | "sit", 26 | "ad", 27 | "adipisicing", 28 | "exercitation" 29 | ], 30 | "friends": [ 31 | { 32 | "id": 0, 33 | "name": "Ruth Holland" 34 | }, 35 | { 36 | "id": 1, 37 | "name": "Ericka Galloway" 38 | }, 39 | { 40 | "id": 2, 41 | "name": "Bates Nicholson" 42 | } 43 | ], 44 | "greeting": "Hello, Larson Duncan! You have 8 unread messages.", 45 | "favoriteFruit": "banana" 46 | }, 47 | { 48 | "_id": "5dddbc7cca27c0d663ca7125", 49 | "index": 1, 50 | "guid": "f35323e5-0085-4a6c-a64c-ffda93e76e5a", 51 | "isActive": false, 52 | "balance": "$3,187.18", 53 | "picture": "http://placehold.it/32x32", 54 | "age": 24, 55 | "eyeColor": "blue", 56 | "name": "Page Bryan", 57 | "gender": "male", 58 | "company": "METROZ", 59 | "email": "pagebryan@metroz.com", 60 | "phone": "+1 (969) 578-2371", 61 | "address": "939 Durland Place, Rosine, Washington, 5116", 62 | "about": "Incididunt ad culpa exercitation incididunt nisi. Irure id commodo aute aliquip aliqua ex mollit duis sunt sunt Lorem sunt fugiat ad. Labore voluptate culpa eu duis aute quis nulla. Ex tempor Lorem anim deserunt occaecat tempor incididunt nulla. Lorem ad nisi veniam do qui minim Lorem aliqua ex duis est excepteur ad. Voluptate minim amet eiusmod ad consectetur occaecat tempor voluptate et ea.\r\n", 63 | "registered": "2019-01-11T08:50:06 -00:00", 64 | "latitude": 28.91494, 65 | "longitude": 119.834963, 66 | "tags": [ 67 | "aliquip", 68 | "ea", 69 | "velit", 70 | "ea", 71 | "aliqua", 72 | "eu", 73 | "quis" 74 | ], 75 | "friends": [ 76 | { 77 | "id": 0, 78 | "name": "Lang Rivers" 79 | }, 80 | { 81 | "id": 1, 82 | "name": "Gallagher Mcdowell" 83 | }, 84 | { 85 | "id": 2, 86 | "name": "Parks Vaughan" 87 | } 88 | ], 89 | "greeting": "Hello, Page Bryan! You have 3 unread messages.", 90 | "favoriteFruit": "strawberry" 91 | }, 92 | { 93 | "_id": "5dddbc7c39466c446a12df91", 94 | "index": 2, 95 | "guid": "9e8d96a0-465d-4a8b-9f86-7b6e1803ce6d", 96 | "isActive": true, 97 | "balance": "$2,618.33", 98 | "picture": "http://placehold.it/32x32", 99 | "age": 33, 100 | "eyeColor": "green", 101 | "name": "Christy Mcknight", 102 | "gender": "female", 103 | "company": "AQUASURE", 104 | "email": "christymcknight@aquasure.com", 105 | "phone": "+1 (979) 443-3555", 106 | "address": "262 Victor Road, Richville, Marshall Islands, 2839", 107 | "about": "Quis id ea in esse. Duis proident do laboris sint Lorem consectetur. Laboris laborum Lorem cillum irure cupidatat do consequat dolor eiusmod sunt fugiat Lorem non irure. Eiusmod culpa cupidatat aliquip laboris elit amet in cupidatat non. Eu incididunt ipsum elit proident enim incididunt id ex qui. Labore veniam culpa ipsum ea quis sit. In labore dolor ullamco excepteur mollit exercitation fugiat duis.\r\n", 108 | "registered": "2017-12-15T08:49:24 -00:00", 109 | "latitude": 60.360453, 110 | "longitude": -80.136358, 111 | "tags": [ 112 | "fugiat", 113 | "nulla", 114 | "exercitation", 115 | "laboris", 116 | "nostrud", 117 | "ad", 118 | "consequat" 119 | ], 120 | "friends": [ 121 | { 122 | "id": 0, 123 | "name": "Jolene Hughes" 124 | }, 125 | { 126 | "id": 1, 127 | "name": "Phillips Burke" 128 | }, 129 | { 130 | "id": 2, 131 | "name": "Morse Bishop" 132 | } 133 | ], 134 | "greeting": "Hello, Christy Mcknight! You have 7 unread messages.", 135 | "favoriteFruit": "banana" 136 | }, 137 | { 138 | "_id": "5dddbc7caa7c35b77e0516db", 139 | "index": 3, 140 | "guid": "083198b0-ce4c-4385-9266-4b496f318ab4", 141 | "isActive": false, 142 | "balance": "$2,990.95", 143 | "picture": "http://placehold.it/32x32", 144 | "age": 32, 145 | "eyeColor": "blue", 146 | "name": "Bowman West", 147 | "gender": "male", 148 | "company": "REALMO", 149 | "email": "bowmanwest@realmo.com", 150 | "phone": "+1 (974) 477-3963", 151 | "address": "840 Leonora Court, Magnolia, Nebraska, 884", 152 | "about": "Dolore ea do voluptate est mollit nostrud fugiat minim mollit duis consectetur tempor dolore. Enim mollit ea quis tempor. Aute sint reprehenderit sunt nostrud ex velit ullamco laborum amet sint eu ut.\r\n", 153 | "registered": "2017-03-26T08:47:16 -01:00", 154 | "latitude": 51.106268, 155 | "longitude": 3.019057, 156 | "tags": [ 157 | "aliqua", 158 | "ex", 159 | "cillum", 160 | "labore", 161 | "exercitation", 162 | "laborum", 163 | "aliqua" 164 | ], 165 | "friends": [ 166 | { 167 | "id": 0, 168 | "name": "Holt Byers" 169 | }, 170 | { 171 | "id": 1, 172 | "name": "Bridget Mcclain" 173 | }, 174 | { 175 | "id": 2, 176 | "name": "Dennis Huff" 177 | } 178 | ], 179 | "greeting": "Hello, Bowman West! You have 9 unread messages.", 180 | "favoriteFruit": "strawberry" 181 | }, 182 | { 183 | "_id": "5dddbc7c774e6126eb259576", 184 | "index": 4, 185 | "guid": "d132b6d6-c220-4b97-bd34-fea4fcbd3505", 186 | "isActive": false, 187 | "balance": "$3,585.85", 188 | "picture": "http://placehold.it/32x32", 189 | "age": 36, 190 | "eyeColor": "blue", 191 | "name": "Candace Hatfield", 192 | "gender": "female", 193 | "company": "GLUKGLUK", 194 | "email": "candacehatfield@glukgluk.com", 195 | "phone": "+1 (805) 551-3177", 196 | "address": "244 Schaefer Street, Goochland, Florida, 2566", 197 | "about": "In consequat laborum ullamco non. Eu nostrud cillum elit tempor dolor. Laborum consectetur eiusmod nulla dolor id mollit culpa. Ad ut dolor culpa voluptate excepteur amet non officia laborum ea nostrud qui. Elit commodo Lorem magna consectetur qui dolore cillum deserunt culpa irure incididunt do minim.\r\n", 198 | "registered": "2018-07-28T04:56:34 -01:00", 199 | "latitude": -51.774446, 200 | "longitude": -21.353908, 201 | "tags": [ 202 | "cupidatat", 203 | "est", 204 | "nulla", 205 | "aute", 206 | "laborum", 207 | "mollit", 208 | "nulla" 209 | ], 210 | "friends": [ 211 | { 212 | "id": 0, 213 | "name": "Barbra Walker" 214 | }, 215 | { 216 | "id": 1, 217 | "name": "Robles Barker" 218 | }, 219 | { 220 | "id": 2, 221 | "name": "Margret Mayer" 222 | } 223 | ], 224 | "greeting": "Hello, Candace Hatfield! You have 8 unread messages.", 225 | "favoriteFruit": "strawberry" 226 | }, 227 | { 228 | "_id": "5dddbc7c071b30e049cfc6b3", 229 | "index": 5, 230 | "guid": "abf28f33-096a-45e1-89ed-e12ebb39d1f0", 231 | "isActive": true, 232 | "balance": "$1,480.95", 233 | "picture": "http://placehold.it/32x32", 234 | "age": 28, 235 | "eyeColor": "green", 236 | "name": "Shelia Riddle", 237 | "gender": "female", 238 | "company": "PULZE", 239 | "email": "sheliariddle@pulze.com", 240 | "phone": "+1 (975) 441-3995", 241 | "address": "492 Canal Avenue, Hasty, Hawaii, 794", 242 | "about": "Cillum voluptate nisi nisi et consequat id non consequat. Lorem quis cupidatat velit ut tempor occaecat pariatur non pariatur. Sit qui excepteur ipsum ullamco. Lorem fugiat qui sit laboris. Amet exercitation tempor do velit sunt laboris commodo Lorem duis officia et anim aliquip.\r\n", 243 | "registered": "2019-11-03T03:40:28 -00:00", 244 | "latitude": 22.28812, 245 | "longitude": -165.572866, 246 | "tags": [ 247 | "anim", 248 | "mollit", 249 | "dolore", 250 | "aliqua", 251 | "cillum", 252 | "adipisicing", 253 | "laboris" 254 | ], 255 | "friends": [ 256 | { 257 | "id": 0, 258 | "name": "Fanny Swanson" 259 | }, 260 | { 261 | "id": 1, 262 | "name": "Annmarie Marks" 263 | }, 264 | { 265 | "id": 2, 266 | "name": "Alejandra Bush" 267 | } 268 | ], 269 | "greeting": "Hello, Shelia Riddle! You have 2 unread messages.", 270 | "favoriteFruit": "strawberry" 271 | }, 272 | { 273 | "_id": "5dddbc7c5ce23f7f4bce9e85", 274 | "index": 6, 275 | "guid": "0a27e8af-3fe7-4148-a841-a6e29ba01468", 276 | "isActive": true, 277 | "balance": "$2,981.89", 278 | "picture": "http://placehold.it/32x32", 279 | "age": 30, 280 | "eyeColor": "blue", 281 | "name": "Marcie Patton", 282 | "gender": "female", 283 | "company": "BOVIS", 284 | "email": "marciepatton@bovis.com", 285 | "phone": "+1 (924) 506-2614", 286 | "address": "942 Church Lane, Kapowsin, New Jersey, 9596", 287 | "about": "Aute anim aliqua irure ipsum veniam. Adipisicing anim et irure veniam labore consequat. In minim incididunt esse quis fugiat eiusmod eiusmod adipisicing ullamco eiusmod anim excepteur aliqua irure.\r\n", 288 | "registered": "2016-09-01T08:25:24 -01:00", 289 | "latitude": 54.033674, 290 | "longitude": -132.006222, 291 | "tags": [ 292 | "aliquip", 293 | "ipsum", 294 | "voluptate", 295 | "voluptate", 296 | "do", 297 | "consequat", 298 | "aliqua" 299 | ], 300 | "friends": [ 301 | { 302 | "id": 0, 303 | "name": "Combs Mullen" 304 | }, 305 | { 306 | "id": 1, 307 | "name": "Brandi Duran" 308 | }, 309 | { 310 | "id": 2, 311 | "name": "Mable Wiggins" 312 | } 313 | ], 314 | "greeting": "Hello, Marcie Patton! You have 4 unread messages.", 315 | "favoriteFruit": "apple" 316 | }, 317 | { 318 | "_id": "5dddbc7c4c4b40d539c340d0", 319 | "index": 7, 320 | "guid": "477f0234-1a64-42af-b913-6150312807d8", 321 | "isActive": false, 322 | "balance": "$1,559.63", 323 | "picture": "http://placehold.it/32x32", 324 | "age": 34, 325 | "eyeColor": "blue", 326 | "name": "Adkins English", 327 | "gender": "male", 328 | "company": "GENESYNK", 329 | "email": "adkinsenglish@genesynk.com", 330 | "phone": "+1 (805) 463-2821", 331 | "address": "415 Louis Place, Ernstville, Tennessee, 2039", 332 | "about": "Veniam cupidatat commodo laboris commodo ut aute anim in Lorem Lorem ea. Lorem dolor tempor ut qui aliquip minim est irure nisi et. Minim consequat esse voluptate eu officia aliqua proident veniam Lorem pariatur ipsum veniam consectetur deserunt. Mollit nulla commodo tempor laboris ea. Ad pariatur et sunt excepteur velit enim. Excepteur sunt id eu labore veniam nostrud commodo. Magna cupidatat do irure ad nulla.\r\n", 333 | "registered": "2017-03-06T06:53:10 -00:00", 334 | "latitude": 60.816632, 335 | "longitude": -28.64763, 336 | "tags": [ 337 | "adipisicing", 338 | "nostrud", 339 | "voluptate", 340 | "incididunt", 341 | "reprehenderit", 342 | "consequat", 343 | "quis" 344 | ], 345 | "friends": [ 346 | { 347 | "id": 0, 348 | "name": "Hill Rosales" 349 | }, 350 | { 351 | "id": 1, 352 | "name": "Black Garza" 353 | }, 354 | { 355 | "id": 2, 356 | "name": "Susie Yang" 357 | } 358 | ], 359 | "greeting": "Hello, Adkins English! You have 9 unread messages.", 360 | "favoriteFruit": "banana" 361 | }, 362 | { 363 | "_id": "5dddbc7cfdc8a6cc31efc5a1", 364 | "index": 8, 365 | "guid": "2bc42313-66fd-41c6-a5c2-5bb9c9d31bbe", 366 | "isActive": false, 367 | "balance": "$3,342.34", 368 | "picture": "http://placehold.it/32x32", 369 | "age": 21, 370 | "eyeColor": "green", 371 | "name": "Lynette Thomas", 372 | "gender": "female", 373 | "company": "OLUCORE", 374 | "email": "lynettethomas@olucore.com", 375 | "phone": "+1 (841) 429-2700", 376 | "address": "736 Oceanview Avenue, Lodoga, Puerto Rico, 1893", 377 | "about": "Et voluptate excepteur consectetur culpa aliqua exercitation reprehenderit occaecat cupidatat. Do adipisicing dolor ipsum magna laborum. Anim consectetur elit aute id elit anim labore ad minim velit in elit ex.\r\n", 378 | "registered": "2016-11-03T09:56:03 -00:00", 379 | "latitude": 42.694344, 380 | "longitude": 125.74787, 381 | "tags": [ 382 | "officia", 383 | "mollit", 384 | "id", 385 | "laborum", 386 | "ipsum", 387 | "eiusmod", 388 | "pariatur" 389 | ], 390 | "friends": [ 391 | { 392 | "id": 0, 393 | "name": "Rosemarie Lyons" 394 | }, 395 | { 396 | "id": 1, 397 | "name": "Allie Maldonado" 398 | }, 399 | { 400 | "id": 2, 401 | "name": "Phoebe Melendez" 402 | } 403 | ], 404 | "greeting": "Hello, Lynette Thomas! You have 10 unread messages.", 405 | "favoriteFruit": "banana" 406 | }, 407 | { 408 | "_id": "5dddbc7c24d58411de399133", 409 | "index": 9, 410 | "guid": "c4a76e3c-7c02-4d7a-9ddb-d97c5ed245a8", 411 | "isActive": false, 412 | "balance": "$3,767.12", 413 | "picture": "http://placehold.it/32x32", 414 | "age": 40, 415 | "eyeColor": "blue", 416 | "name": "Susanne Bonner", 417 | "gender": "female", 418 | "company": "FIBEROX", 419 | "email": "susannebonner@fiberox.com", 420 | "phone": "+1 (929) 464-2215", 421 | "address": "807 Calder Place, Selma, Iowa, 6801", 422 | "about": "Commodo anim labore ea qui occaecat consequat pariatur dolor adipisicing. Duis voluptate laborum aute magna ex sunt. Adipisicing laborum labore exercitation proident ut nulla duis nostrud Lorem aliqua anim. Eu proident eu cupidatat labore fugiat reprehenderit. Amet aute consectetur aliquip fugiat aute. In sit eu aliquip aliqua cillum fugiat in ad irure consequat duis ad commodo officia. Laboris officia dolor magna fugiat in excepteur sit ea.\r\n", 423 | "registered": "2016-09-13T09:04:09 -01:00", 424 | "latitude": 86.587241, 425 | "longitude": 116.940778, 426 | "tags": [ 427 | "deserunt", 428 | "proident", 429 | "laborum", 430 | "excepteur", 431 | "amet", 432 | "minim", 433 | "deserunt" 434 | ], 435 | "friends": [ 436 | { 437 | "id": 0, 438 | "name": "Lois Roberts" 439 | }, 440 | { 441 | "id": 1, 442 | "name": "Snyder Leblanc" 443 | }, 444 | { 445 | "id": 2, 446 | "name": "Darla Drake" 447 | } 448 | ], 449 | "greeting": "Hello, Susanne Bonner! You have 9 unread messages.", 450 | "favoriteFruit": "apple" 451 | }, 452 | { 453 | "_id": "5dddbc7cfb178436a216ecbd", 454 | "index": 10, 455 | "guid": "6d9ca511-a297-4dcb-9de4-3674a42323b8", 456 | "isActive": false, 457 | "balance": "$1,589.78", 458 | "picture": "http://placehold.it/32x32", 459 | "age": 33, 460 | "eyeColor": "brown", 461 | "name": "Lola Wooten", 462 | "gender": "female", 463 | "company": "GEEKFARM", 464 | "email": "lolawooten@geekfarm.com", 465 | "phone": "+1 (993) 539-2481", 466 | "address": "426 Holmes Lane, Lawrence, Vermont, 6804", 467 | "about": "Minim labore excepteur ea magna culpa labore duis aliqua proident ut eiusmod enim ex. Aliquip duis minim fugiat qui Lorem consequat est in proident veniam laboris deserunt sit. Id duis do sit incididunt duis non qui ut sunt aute non id enim exercitation.\r\n", 468 | "registered": "2018-03-11T11:49:50 -00:00", 469 | "latitude": -28.575048, 470 | "longitude": 111.709022, 471 | "tags": [ 472 | "dolor", 473 | "anim", 474 | "proident", 475 | "cupidatat", 476 | "do", 477 | "veniam", 478 | "in" 479 | ], 480 | "friends": [ 481 | { 482 | "id": 0, 483 | "name": "Alison Odom" 484 | }, 485 | { 486 | "id": 1, 487 | "name": "Lorena Henderson" 488 | }, 489 | { 490 | "id": 2, 491 | "name": "Stanton Oneill" 492 | } 493 | ], 494 | "greeting": "Hello, Lola Wooten! You have 5 unread messages.", 495 | "favoriteFruit": "strawberry" 496 | }, 497 | { 498 | "_id": "5dddbc7cdee54c7f40c7a2a1", 499 | "index": 11, 500 | "guid": "ca781559-d0aa-476e-83ff-89b2911fc24e", 501 | "isActive": false, 502 | "balance": "$3,215.53", 503 | "picture": "http://placehold.it/32x32", 504 | "age": 38, 505 | "eyeColor": "blue", 506 | "name": "Mary Goodman", 507 | "gender": "female", 508 | "company": "VOIPA", 509 | "email": "marygoodman@voipa.com", 510 | "phone": "+1 (873) 573-2085", 511 | "address": "844 Clarendon Road, Bordelonville, Oklahoma, 6617", 512 | "about": "Ea quis pariatur labore deserunt sit duis do incididunt. Culpa irure adipisicing qui labore enim anim ea laboris irure elit laboris cillum. Laborum proident occaecat quis pariatur. Ad culpa velit deserunt consectetur enim velit labore esse do.\r\n", 513 | "registered": "2019-04-18T11:32:34 -01:00", 514 | "latitude": 36.352539, 515 | "longitude": -36.562717, 516 | "tags": [ 517 | "qui", 518 | "exercitation", 519 | "non", 520 | "cillum", 521 | "ipsum", 522 | "nulla", 523 | "magna" 524 | ], 525 | "friends": [ 526 | { 527 | "id": 0, 528 | "name": "Alyssa Key" 529 | }, 530 | { 531 | "id": 1, 532 | "name": "Flora Rosa" 533 | }, 534 | { 535 | "id": 2, 536 | "name": "Rebecca Mccoy" 537 | } 538 | ], 539 | "greeting": "Hello, Mary Goodman! You have 5 unread messages.", 540 | "favoriteFruit": "banana" 541 | }, 542 | { 543 | "_id": "5dddbc7c70b8d00e261b42cd", 544 | "index": 12, 545 | "guid": "02541752-488c-4737-a405-b91c4e4748a9", 546 | "isActive": true, 547 | "balance": "$1,268.22", 548 | "picture": "http://placehold.it/32x32", 549 | "age": 28, 550 | "eyeColor": "brown", 551 | "name": "Lottie Flores", 552 | "gender": "female", 553 | "company": "VIXO", 554 | "email": "lottieflores@vixo.com", 555 | "phone": "+1 (873) 583-2052", 556 | "address": "924 Bergen Place, Roulette, Michigan, 8164", 557 | "about": "Mollit laboris eiusmod esse eiusmod ipsum velit officia adipisicing. Tempor culpa tempor enim voluptate eiusmod. Dolore culpa laboris do sint veniam aliqua cupidatat aute.\r\n", 558 | "registered": "2019-06-11T02:15:15 -01:00", 559 | "latitude": 56.697401, 560 | "longitude": 116.687927, 561 | "tags": [ 562 | "exercitation", 563 | "sunt", 564 | "magna", 565 | "anim", 566 | "est", 567 | "ex", 568 | "ex" 569 | ], 570 | "friends": [ 571 | { 572 | "id": 0, 573 | "name": "Kent Wiley" 574 | }, 575 | { 576 | "id": 1, 577 | "name": "Nichols Koch" 578 | }, 579 | { 580 | "id": 2, 581 | "name": "Letha Underwood" 582 | } 583 | ], 584 | "greeting": "Hello, Lottie Flores! You have 10 unread messages.", 585 | "favoriteFruit": "strawberry" 586 | }, 587 | { 588 | "_id": "5dddbc7cf182c20c2b785e41", 589 | "index": 13, 590 | "guid": "a3da2a65-eee1-401e-8325-70221389657b", 591 | "isActive": false, 592 | "balance": "$3,054.26", 593 | "picture": "http://placehold.it/32x32", 594 | "age": 21, 595 | "eyeColor": "brown", 596 | "name": "Christa Cantrell", 597 | "gender": "female", 598 | "company": "DOGTOWN", 599 | "email": "christacantrell@dogtown.com", 600 | "phone": "+1 (987) 513-3807", 601 | "address": "432 Blake Avenue, Dale, Maine, 3113", 602 | "about": "Laboris et laboris magna mollit tempor ea aute pariatur incididunt duis mollit. Lorem culpa sit in aute. Est voluptate in et pariatur in ad eu anim non est exercitation anim. Voluptate mollit magna est pariatur irure tempor minim velit. Adipisicing deserunt elit dolor irure non consequat. Culpa consequat deserunt culpa incididunt incididunt ipsum ipsum fugiat culpa ad labore Lorem laborum. Ad culpa occaecat occaecat do.\r\n", 603 | "registered": "2015-05-09T09:45:32 -01:00", 604 | "latitude": 28.212992, 605 | "longitude": -49.317963, 606 | "tags": [ 607 | "id", 608 | "ut", 609 | "in", 610 | "veniam", 611 | "nisi", 612 | "sint", 613 | "amet" 614 | ], 615 | "friends": [ 616 | { 617 | "id": 0, 618 | "name": "Sadie Oliver" 619 | }, 620 | { 621 | "id": 1, 622 | "name": "Pace Hester" 623 | }, 624 | { 625 | "id": 2, 626 | "name": "Lily Fulton" 627 | } 628 | ], 629 | "greeting": "Hello, Christa Cantrell! You have 7 unread messages.", 630 | "favoriteFruit": "banana" 631 | }, 632 | { 633 | "_id": "5dddbc7c1ac87fe2d5de2f0d", 634 | "index": 14, 635 | "guid": "4bb20f8a-b1e6-4490-b42f-c644f0e120a7", 636 | "isActive": true, 637 | "balance": "$3,709.30", 638 | "picture": "http://placehold.it/32x32", 639 | "age": 26, 640 | "eyeColor": "green", 641 | "name": "Bishop Clarke", 642 | "gender": "male", 643 | "company": "FLOTONIC", 644 | "email": "bishopclarke@flotonic.com", 645 | "phone": "+1 (978) 536-3954", 646 | "address": "670 Glenwood Road, Rodman, Massachusetts, 8432", 647 | "about": "Nulla irure reprehenderit excepteur proident excepteur ullamco laboris incididunt irure cupidatat. Et esse cillum minim voluptate anim do ullamco aliqua in. Ut aliqua duis mollit aliqua incididunt est ea labore adipisicing nulla anim sit eiusmod. Incididunt consequat consequat quis id do laborum est laborum.\r\n", 648 | "registered": "2019-08-26T10:04:18 -01:00", 649 | "latitude": 30.862923, 650 | "longitude": 77.381253, 651 | "tags": [ 652 | "duis", 653 | "culpa", 654 | "elit", 655 | "excepteur", 656 | "culpa", 657 | "est", 658 | "voluptate" 659 | ], 660 | "friends": [ 661 | { 662 | "id": 0, 663 | "name": "Shauna Mcconnell" 664 | }, 665 | { 666 | "id": 1, 667 | "name": "Yesenia Moses" 668 | }, 669 | { 670 | "id": 2, 671 | "name": "Patrice Rasmussen" 672 | } 673 | ], 674 | "greeting": "Hello, Bishop Clarke! You have 8 unread messages.", 675 | "favoriteFruit": "strawberry" 676 | }, 677 | { 678 | "_id": "5dddbc7c0944d3f52b37785c", 679 | "index": 15, 680 | "guid": "4e0db110-20b6-426a-991f-5bd86abfe2e9", 681 | "isActive": true, 682 | "balance": "$2,429.62", 683 | "picture": "http://placehold.it/32x32", 684 | "age": 21, 685 | "eyeColor": "green", 686 | "name": "Reynolds Bernard", 687 | "gender": "male", 688 | "company": "KINETICUT", 689 | "email": "reynoldsbernard@kineticut.com", 690 | "phone": "+1 (849) 480-2270", 691 | "address": "364 Suydam Street, Belvoir, Palau, 6266", 692 | "about": "In nisi sint est nulla. Aliqua eu labore ad tempor dolor reprehenderit cillum sit in ut nisi minim exercitation. Esse sunt sint sint nulla ut aliquip sint duis Lorem magna eu officia ullamco.\r\n", 693 | "registered": "2014-09-22T09:25:55 -01:00", 694 | "latitude": 63.730402, 695 | "longitude": 41.135445, 696 | "tags": [ 697 | "officia", 698 | "do", 699 | "deserunt", 700 | "officia", 701 | "ad", 702 | "eiusmod", 703 | "in" 704 | ], 705 | "friends": [ 706 | { 707 | "id": 0, 708 | "name": "David Phelps" 709 | }, 710 | { 711 | "id": 1, 712 | "name": "Hobbs Emerson" 713 | }, 714 | { 715 | "id": 2, 716 | "name": "Margie Webb" 717 | } 718 | ], 719 | "greeting": "Hello, Reynolds Bernard! You have 3 unread messages.", 720 | "favoriteFruit": "strawberry" 721 | }, 722 | { 723 | "_id": "5dddbc7cdf2b268e2d69fdca", 724 | "index": 16, 725 | "guid": "def1b7b1-c822-42c5-b236-bf176ed7849e", 726 | "isActive": false, 727 | "balance": "$3,825.26", 728 | "picture": "http://placehold.it/32x32", 729 | "age": 33, 730 | "eyeColor": "brown", 731 | "name": "Whitaker Harrington", 732 | "gender": "male", 733 | "company": "VORTEXACO", 734 | "email": "whitakerharrington@vortexaco.com", 735 | "phone": "+1 (941) 423-3819", 736 | "address": "217 Kenmore Court, Tilleda, American Samoa, 6235", 737 | "about": "Elit cupidatat sunt non adipisicing cupidatat nostrud et eu. Nostrud elit occaecat incididunt fugiat mollit commodo exercitation dolor amet esse. Veniam voluptate ea cupidatat qui excepteur sit ea est ut eiusmod qui ullamco. Ad labore eu ipsum minim culpa magna voluptate.\r\n", 738 | "registered": "2017-06-17T10:28:52 -01:00", 739 | "latitude": -46.053527, 740 | "longitude": 137.948648, 741 | "tags": [ 742 | "exercitation", 743 | "aliqua", 744 | "in", 745 | "commodo", 746 | "pariatur", 747 | "aliqua", 748 | "mollit" 749 | ], 750 | "friends": [ 751 | { 752 | "id": 0, 753 | "name": "Cox Wallace" 754 | }, 755 | { 756 | "id": 1, 757 | "name": "Lyons Ramos" 758 | }, 759 | { 760 | "id": 2, 761 | "name": "Trudy Hunt" 762 | } 763 | ], 764 | "greeting": "Hello, Whitaker Harrington! You have 5 unread messages.", 765 | "favoriteFruit": "apple" 766 | }, 767 | { 768 | "_id": "5dddbc7cf6744d3397d87061", 769 | "index": 17, 770 | "guid": "eca770d3-5f72-40f6-903e-0ade93c3d4ea", 771 | "isActive": false, 772 | "balance": "$3,871.76", 773 | "picture": "http://placehold.it/32x32", 774 | "age": 30, 775 | "eyeColor": "green", 776 | "name": "Gillespie Wright", 777 | "gender": "male", 778 | "company": "BITREX", 779 | "email": "gillespiewright@bitrex.com", 780 | "phone": "+1 (952) 468-2089", 781 | "address": "840 Adelphi Street, Newkirk, Missouri, 7535", 782 | "about": "Id magna reprehenderit ipsum eu excepteur adipisicing exercitation Lorem esse. Consequat non cupidatat pariatur aliquip eiusmod elit qui eu esse occaecat commodo adipisicing. Labore laboris sunt magna in nostrud ipsum consequat esse occaecat. Qui non deserunt ex ex ut eu velit id reprehenderit culpa veniam proident. Exercitation irure id sit sit.\r\n", 783 | "registered": "2018-01-12T08:47:57 -00:00", 784 | "latitude": -6.155754, 785 | "longitude": -27.23451, 786 | "tags": [ 787 | "reprehenderit", 788 | "dolor", 789 | "laboris", 790 | "consequat", 791 | "ea", 792 | "commodo", 793 | "aliquip" 794 | ], 795 | "friends": [ 796 | { 797 | "id": 0, 798 | "name": "Macdonald Mccullough" 799 | }, 800 | { 801 | "id": 1, 802 | "name": "Gabriela Benjamin" 803 | }, 804 | { 805 | "id": 2, 806 | "name": "Lawson Hart" 807 | } 808 | ], 809 | "greeting": "Hello, Gillespie Wright! You have 4 unread messages.", 810 | "favoriteFruit": "strawberry" 811 | }, 812 | { 813 | "_id": "5dddbc7c8cb763236c144323", 814 | "index": 18, 815 | "guid": "ae32ad7e-bf2e-4ae7-a597-d07fe3c5738c", 816 | "isActive": true, 817 | "balance": "$1,005.43", 818 | "picture": "http://placehold.it/32x32", 819 | "age": 24, 820 | "eyeColor": "blue", 821 | "name": "Vaughan Richmond", 822 | "gender": "male", 823 | "company": "KOOGLE", 824 | "email": "vaughanrichmond@koogle.com", 825 | "phone": "+1 (979) 500-3259", 826 | "address": "664 Orange Street, Mappsville, Indiana, 5227", 827 | "about": "Anim labore eu consequat minim id. Reprehenderit ullamco deserunt consectetur aute Lorem elit ea. Tempor deserunt in ullamco cupidatat sunt pariatur eu aute sint fugiat sunt esse sint. Consectetur magna incididunt ea ad adipisicing mollit duis esse culpa laborum labore elit laboris. Do laboris culpa esse eu dolor.\r\n", 828 | "registered": "2014-10-03T10:51:00 -01:00", 829 | "latitude": -3.884184, 830 | "longitude": 179.759725, 831 | "tags": [ 832 | "laboris", 833 | "mollit", 834 | "do", 835 | "ea", 836 | "do", 837 | "fugiat", 838 | "officia" 839 | ], 840 | "friends": [ 841 | { 842 | "id": 0, 843 | "name": "Peggy Browning" 844 | }, 845 | { 846 | "id": 1, 847 | "name": "Neal Rowe" 848 | }, 849 | { 850 | "id": 2, 851 | "name": "Santos Burch" 852 | } 853 | ], 854 | "greeting": "Hello, Vaughan Richmond! You have 3 unread messages.", 855 | "favoriteFruit": "banana" 856 | }, 857 | { 858 | "_id": "5dddbc7c4981f2e1efa1e4ef", 859 | "index": 19, 860 | "guid": "3353f71b-3689-45b2-9cf0-e2063de877ec", 861 | "isActive": false, 862 | "balance": "$3,184.25", 863 | "picture": "http://placehold.it/32x32", 864 | "age": 27, 865 | "eyeColor": "blue", 866 | "name": "Marianne Shepard", 867 | "gender": "female", 868 | "company": "ASIMILINE", 869 | "email": "marianneshepard@asimiline.com", 870 | "phone": "+1 (957) 459-2085", 871 | "address": "942 Butler Street, Nescatunga, Arkansas, 6557", 872 | "about": "Sunt occaecat ad tempor voluptate irure cillum minim commodo pariatur ad nisi qui ipsum. Irure commodo laboris sint eu excepteur duis adipisicing elit officia officia. Consequat Lorem culpa consequat voluptate dolor culpa occaecat occaecat. Aliquip culpa tempor id sunt reprehenderit cillum esse cillum. Officia culpa amet voluptate ut mollit tempor pariatur laboris sint irure amet anim in et. Anim elit non esse nostrud officia aute et ea eu adipisicing cillum dolore aliqua. Qui culpa veniam velit cillum officia nostrud sunt culpa minim duis.\r\n", 873 | "registered": "2016-11-24T07:51:13 -00:00", 874 | "latitude": 33.689593, 875 | "longitude": 3.183218, 876 | "tags": [ 877 | "voluptate", 878 | "aute", 879 | "consequat", 880 | "minim", 881 | "exercitation", 882 | "in", 883 | "ea" 884 | ], 885 | "friends": [ 886 | { 887 | "id": 0, 888 | "name": "Clarice Hampton" 889 | }, 890 | { 891 | "id": 1, 892 | "name": "Hester Riley" 893 | }, 894 | { 895 | "id": 2, 896 | "name": "Renee Robles" 897 | } 898 | ], 899 | "greeting": "Hello, Marianne Shepard! You have 2 unread messages.", 900 | "favoriteFruit": "apple" 901 | }, 902 | { 903 | "_id": "5dddbc7cd4e2211a7e502aed", 904 | "index": 20, 905 | "guid": "19946427-bb89-4096-80dc-16dedfe74e3a", 906 | "isActive": false, 907 | "balance": "$2,002.78", 908 | "picture": "http://placehold.it/32x32", 909 | "age": 37, 910 | "eyeColor": "brown", 911 | "name": "Perkins Jimenez", 912 | "gender": "male", 913 | "company": "ZYPLE", 914 | "email": "perkinsjimenez@zyple.com", 915 | "phone": "+1 (822) 480-2231", 916 | "address": "507 Lincoln Avenue, Katonah, New York, 9563", 917 | "about": "In dolor dolor in aliquip aliquip minim Lorem ex voluptate sint elit pariatur quis. Culpa reprehenderit ut proident nulla cillum adipisicing qui amet sint aliquip. Cupidatat voluptate adipisicing reprehenderit fugiat in nisi in in quis cillum non laborum nostrud.\r\n", 918 | "registered": "2019-04-28T05:59:23 -01:00", 919 | "latitude": 31.903213, 920 | "longitude": -104.141072, 921 | "tags": [ 922 | "adipisicing", 923 | "nisi", 924 | "est", 925 | "aute", 926 | "quis", 927 | "officia", 928 | "eiusmod" 929 | ], 930 | "friends": [ 931 | { 932 | "id": 0, 933 | "name": "Graves Tate" 934 | }, 935 | { 936 | "id": 1, 937 | "name": "Rosemary Griffin" 938 | }, 939 | { 940 | "id": 2, 941 | "name": "Melisa Ramsey" 942 | } 943 | ], 944 | "greeting": "Hello, Perkins Jimenez! You have 10 unread messages.", 945 | "favoriteFruit": "banana" 946 | }, 947 | { 948 | "_id": "5dddbc7cb4bff0a7543c0f64", 949 | "index": 21, 950 | "guid": "395c04ea-01d8-4f16-8ee5-e86b8bcc5020", 951 | "isActive": false, 952 | "balance": "$1,886.19", 953 | "picture": "http://placehold.it/32x32", 954 | "age": 23, 955 | "eyeColor": "brown", 956 | "name": "Carey Melton", 957 | "gender": "male", 958 | "company": "SENMAO", 959 | "email": "careymelton@senmao.com", 960 | "phone": "+1 (939) 559-2715", 961 | "address": "364 Myrtle Avenue, Sardis, Virginia, 2373", 962 | "about": "Magna dolore magna deserunt irure deserunt quis exercitation do esse anim ipsum. Pariatur sunt et aute velit non laboris amet excepteur fugiat tempor anim in Lorem irure. Excepteur irure id commodo consectetur sunt qui consectetur eiusmod excepteur laborum laborum. In nulla veniam veniam elit laboris commodo enim reprehenderit non.\r\n", 963 | "registered": "2016-02-25T12:25:38 -00:00", 964 | "latitude": 63.673122, 965 | "longitude": -23.609038, 966 | "tags": [ 967 | "consectetur", 968 | "consectetur", 969 | "sit", 970 | "occaecat", 971 | "nisi", 972 | "culpa", 973 | "ea" 974 | ], 975 | "friends": [ 976 | { 977 | "id": 0, 978 | "name": "Kaitlin Harding" 979 | }, 980 | { 981 | "id": 1, 982 | "name": "Christensen Gillespie" 983 | }, 984 | { 985 | "id": 2, 986 | "name": "Griffith Dominguez" 987 | } 988 | ], 989 | "greeting": "Hello, Carey Melton! You have 7 unread messages.", 990 | "favoriteFruit": "strawberry" 991 | }, 992 | { 993 | "_id": "5dddbc7c14bae168c0b912eb", 994 | "index": 22, 995 | "guid": "aef97f37-04af-4bff-b90f-d99ea7228453", 996 | "isActive": false, 997 | "balance": "$2,010.36", 998 | "picture": "http://placehold.it/32x32", 999 | "age": 35, 1000 | "eyeColor": "green", 1001 | "name": "Keisha Bauer", 1002 | "gender": "female", 1003 | "company": "ENDIPIN", 1004 | "email": "keishabauer@endipin.com", 1005 | "phone": "+1 (828) 434-2272", 1006 | "address": "167 Newel Street, Shepardsville, Illinois, 3661", 1007 | "about": "Ut reprehenderit consequat tempor duis qui. Magna minim proident est qui dolor ut mollit ullamco excepteur magna qui est adipisicing veniam. Quis esse duis ex amet nulla sint commodo voluptate in ullamco veniam laboris non. Laboris irure ad cupidatat id consectetur amet anim irure sint ut. Eiusmod eu ex sint quis duis sunt minim Lorem incididunt est minim. Dolore sint magna amet ut veniam laboris nisi esse amet nostrud amet nostrud ex incididunt.\r\n", 1008 | "registered": "2016-03-17T07:34:08 -00:00", 1009 | "latitude": -89.034194, 1010 | "longitude": 19.001931, 1011 | "tags": [ 1012 | "ex", 1013 | "minim", 1014 | "minim", 1015 | "consectetur", 1016 | "aliqua", 1017 | "quis", 1018 | "sit" 1019 | ], 1020 | "friends": [ 1021 | { 1022 | "id": 0, 1023 | "name": "Robin Beasley" 1024 | }, 1025 | { 1026 | "id": 1, 1027 | "name": "Claire Salinas" 1028 | }, 1029 | { 1030 | "id": 2, 1031 | "name": "Imelda Austin" 1032 | } 1033 | ], 1034 | "greeting": "Hello, Keisha Bauer! You have 8 unread messages.", 1035 | "favoriteFruit": "banana" 1036 | }, 1037 | { 1038 | "_id": "5dddbc7cdc3e46116ae9ec24", 1039 | "index": 23, 1040 | "guid": "21154a5c-36cb-46a4-8897-13629657f907", 1041 | "isActive": false, 1042 | "balance": "$2,413.52", 1043 | "picture": "http://placehold.it/32x32", 1044 | "age": 30, 1045 | "eyeColor": "blue", 1046 | "name": "Virgie Buckner", 1047 | "gender": "female", 1048 | "company": "ARTIQ", 1049 | "email": "virgiebuckner@artiq.com", 1050 | "phone": "+1 (814) 404-3922", 1051 | "address": "620 Fillmore Avenue, Tioga, District Of Columbia, 2643", 1052 | "about": "Id minim aliqua consectetur nostrud aute ad commodo eiusmod dolor proident. Deserunt dolore culpa mollit exercitation ex duis est ea magna culpa incididunt est do. Occaecat exercitation proident sunt est ullamco Lorem Lorem voluptate. Fugiat ipsum dolor do est deserunt quis culpa aliqua do duis esse aute id.\r\n", 1053 | "registered": "2017-08-04T08:46:38 -01:00", 1054 | "latitude": 80.174193, 1055 | "longitude": 143.283761, 1056 | "tags": [ 1057 | "sunt", 1058 | "laborum", 1059 | "velit", 1060 | "excepteur", 1061 | "nulla", 1062 | "ut", 1063 | "incididunt" 1064 | ], 1065 | "friends": [ 1066 | { 1067 | "id": 0, 1068 | "name": "Kay Padilla" 1069 | }, 1070 | { 1071 | "id": 1, 1072 | "name": "Brown Noel" 1073 | }, 1074 | { 1075 | "id": 2, 1076 | "name": "Torres Beard" 1077 | } 1078 | ], 1079 | "greeting": "Hello, Virgie Buckner! You have 1 unread messages.", 1080 | "favoriteFruit": "banana" 1081 | }, 1082 | { 1083 | "_id": "5dddbc7cd886fae4e803de18", 1084 | "index": 24, 1085 | "guid": "63c78341-027a-4910-b631-017b53f8bcd2", 1086 | "isActive": true, 1087 | "balance": "$2,628.36", 1088 | "picture": "http://placehold.it/32x32", 1089 | "age": 33, 1090 | "eyeColor": "green", 1091 | "name": "Cash Bond", 1092 | "gender": "male", 1093 | "company": "DANJA", 1094 | "email": "cashbond@danja.com", 1095 | "phone": "+1 (942) 574-2754", 1096 | "address": "205 Flatlands Avenue, Sanborn, Pennsylvania, 9654", 1097 | "about": "Culpa est deserunt irure et aute laboris ullamco aute. Eu dolor dolor irure qui eu eu. Mollit proident fugiat exercitation ipsum id ad consectetur tempor aute proident exercitation incididunt. Laboris eu et in culpa non ea nisi elit.\r\n", 1098 | "registered": "2015-07-11T02:45:29 -01:00", 1099 | "latitude": -36.985461, 1100 | "longitude": 41.202789, 1101 | "tags": [ 1102 | "fugiat", 1103 | "deserunt", 1104 | "commodo", 1105 | "et", 1106 | "ex", 1107 | "aliquip", 1108 | "ullamco" 1109 | ], 1110 | "friends": [ 1111 | { 1112 | "id": 0, 1113 | "name": "Ray Fox" 1114 | }, 1115 | { 1116 | "id": 1, 1117 | "name": "Katelyn Burnett" 1118 | }, 1119 | { 1120 | "id": 2, 1121 | "name": "Swanson Frederick" 1122 | } 1123 | ], 1124 | "greeting": "Hello, Cash Bond! You have 6 unread messages.", 1125 | "favoriteFruit": "banana" 1126 | }, 1127 | { 1128 | "_id": "5dddbc7c6b423213adb12207", 1129 | "index": 25, 1130 | "guid": "b0409698-d854-4814-8bb9-8f0cc52c2f50", 1131 | "isActive": true, 1132 | "balance": "$1,963.87", 1133 | "picture": "http://placehold.it/32x32", 1134 | "age": 25, 1135 | "eyeColor": "green", 1136 | "name": "Andrews Cross", 1137 | "gender": "male", 1138 | "company": "LIMAGE", 1139 | "email": "andrewscross@limage.com", 1140 | "phone": "+1 (985) 572-2748", 1141 | "address": "989 Hoyts Lane, Hollins, North Dakota, 4096", 1142 | "about": "Et et labore dolor sit ea non anim. Deserunt veniam id qui commodo ipsum consectetur. Sunt sunt ullamco tempor labore cillum quis deserunt ad voluptate aliquip nulla. Voluptate aute mollit exercitation officia consectetur proident voluptate consequat Lorem ullamco eu est cillum aliquip.\r\n", 1143 | "registered": "2017-11-17T07:25:59 -00:00", 1144 | "latitude": -14.050703, 1145 | "longitude": -49.518653, 1146 | "tags": [ 1147 | "laborum", 1148 | "excepteur", 1149 | "veniam", 1150 | "laboris", 1151 | "exercitation", 1152 | "ea", 1153 | "occaecat" 1154 | ], 1155 | "friends": [ 1156 | { 1157 | "id": 0, 1158 | "name": "Harper Rodgers" 1159 | }, 1160 | { 1161 | "id": 1, 1162 | "name": "Ella Valdez" 1163 | }, 1164 | { 1165 | "id": 2, 1166 | "name": "Maggie Barry" 1167 | } 1168 | ], 1169 | "greeting": "Hello, Andrews Cross! You have 7 unread messages.", 1170 | "favoriteFruit": "banana" 1171 | }, 1172 | { 1173 | "_id": "5dddbc7c4dd7c97a244d0cbd", 1174 | "index": 26, 1175 | "guid": "1561301a-be9c-4ba9-b717-0da0ef3867fc", 1176 | "isActive": false, 1177 | "balance": "$1,329.29", 1178 | "picture": "http://placehold.it/32x32", 1179 | "age": 33, 1180 | "eyeColor": "brown", 1181 | "name": "Tracie Goodwin", 1182 | "gender": "female", 1183 | "company": "PAPRICUT", 1184 | "email": "traciegoodwin@papricut.com", 1185 | "phone": "+1 (844) 543-3794", 1186 | "address": "507 Prescott Place, Hayes, Northern Mariana Islands, 2809", 1187 | "about": "Officia officia voluptate ullamco voluptate fugiat aliqua dolor nulla mollit sit non proident. Aute esse cillum pariatur non pariatur exercitation. Velit ex nulla cillum ullamco laborum ullamco.\r\n", 1188 | "registered": "2019-10-14T03:39:04 -01:00", 1189 | "latitude": 11.399166, 1190 | "longitude": 21.576999, 1191 | "tags": [ 1192 | "ea", 1193 | "dolore", 1194 | "est", 1195 | "in", 1196 | "Lorem", 1197 | "nisi", 1198 | "nostrud" 1199 | ], 1200 | "friends": [ 1201 | { 1202 | "id": 0, 1203 | "name": "Janice Leach" 1204 | }, 1205 | { 1206 | "id": 1, 1207 | "name": "Schroeder Dunlap" 1208 | }, 1209 | { 1210 | "id": 2, 1211 | "name": "Bernice Orr" 1212 | } 1213 | ], 1214 | "greeting": "Hello, Tracie Goodwin! You have 3 unread messages.", 1215 | "favoriteFruit": "strawberry" 1216 | }, 1217 | { 1218 | "_id": "5dddbc7caee9b218838f7349", 1219 | "index": 27, 1220 | "guid": "6e0e4123-e39f-4eda-896f-5991fd38821b", 1221 | "isActive": true, 1222 | "balance": "$3,485.92", 1223 | "picture": "http://placehold.it/32x32", 1224 | "age": 25, 1225 | "eyeColor": "brown", 1226 | "name": "Norman Stafford", 1227 | "gender": "male", 1228 | "company": "ZENTURY", 1229 | "email": "normanstafford@zentury.com", 1230 | "phone": "+1 (976) 532-2607", 1231 | "address": "519 Vanderveer Place, Cazadero, Kansas, 6751", 1232 | "about": "Ad duis aute sit officia deserunt ut sunt qui commodo ea irure id mollit aliqua. Ullamco enim amet dolor enim aliquip irure cupidatat. Et pariatur veniam labore fugiat. Esse mollit consectetur nulla ex esse ut. Est reprehenderit cillum minim mollit cillum ea dolore est et elit velit sunt Lorem. Tempor incididunt et officia id duis amet est Lorem nostrud commodo qui esse. Non ea ullamco minim officia qui in elit pariatur.\r\n", 1233 | "registered": "2019-01-10T04:16:52 -00:00", 1234 | "latitude": 62.895244, 1235 | "longitude": 82.100056, 1236 | "tags": [ 1237 | "mollit", 1238 | "adipisicing", 1239 | "excepteur", 1240 | "non", 1241 | "et", 1242 | "cillum", 1243 | "dolore" 1244 | ], 1245 | "friends": [ 1246 | { 1247 | "id": 0, 1248 | "name": "Sloan Olson" 1249 | }, 1250 | { 1251 | "id": 1, 1252 | "name": "Benson Rodriguez" 1253 | }, 1254 | { 1255 | "id": 2, 1256 | "name": "Hayden Cummings" 1257 | } 1258 | ], 1259 | "greeting": "Hello, Norman Stafford! You have 8 unread messages.", 1260 | "favoriteFruit": "banana" 1261 | }, 1262 | { 1263 | "_id": "5dddbc7ca513a9c72dc679b7", 1264 | "index": 28, 1265 | "guid": "8dd795b7-90e7-4746-adc4-3131bba34614", 1266 | "isActive": false, 1267 | "balance": "$3,703.58", 1268 | "picture": "http://placehold.it/32x32", 1269 | "age": 21, 1270 | "eyeColor": "green", 1271 | "name": "Zimmerman Vargas", 1272 | "gender": "male", 1273 | "company": "KATAKANA", 1274 | "email": "zimmermanvargas@katakana.com", 1275 | "phone": "+1 (843) 423-3926", 1276 | "address": "915 Vanderbilt Street, Tyhee, Connecticut, 3588", 1277 | "about": "Ea quis aliquip sit duis incididunt deserunt et aliqua aliquip eu sit ipsum reprehenderit do. Do id Lorem velit nulla eu consectetur Lorem amet Lorem voluptate. Reprehenderit dolor pariatur anim elit exercitation nostrud esse. Minim irure commodo duis consequat ex mollit do ipsum eiusmod tempor id ullamco adipisicing. Sint ullamco eiusmod mollit laboris amet ex eu. Mollit do amet ullamco sint proident cillum Lorem commodo minim adipisicing esse adipisicing elit.\r\n", 1278 | "registered": "2015-04-13T02:55:08 -01:00", 1279 | "latitude": -33.540404, 1280 | "longitude": -6.648554, 1281 | "tags": [ 1282 | "dolore", 1283 | "dolor", 1284 | "commodo", 1285 | "tempor", 1286 | "quis", 1287 | "magna", 1288 | "quis" 1289 | ], 1290 | "friends": [ 1291 | { 1292 | "id": 0, 1293 | "name": "Charmaine Brennan" 1294 | }, 1295 | { 1296 | "id": 1, 1297 | "name": "Merle Wolfe" 1298 | }, 1299 | { 1300 | "id": 2, 1301 | "name": "Tabatha Rocha" 1302 | } 1303 | ], 1304 | "greeting": "Hello, Zimmerman Vargas! You have 7 unread messages.", 1305 | "favoriteFruit": "strawberry" 1306 | }, 1307 | { 1308 | "_id": "5dddbc7c875ee915555311a2", 1309 | "index": 29, 1310 | "guid": "8a8f41ce-c199-4cc8-86f5-e8eb5a00ca5c", 1311 | "isActive": true, 1312 | "balance": "$3,892.92", 1313 | "picture": "http://placehold.it/32x32", 1314 | "age": 21, 1315 | "eyeColor": "brown", 1316 | "name": "Montoya Pugh", 1317 | "gender": "male", 1318 | "company": "DECRATEX", 1319 | "email": "montoyapugh@decratex.com", 1320 | "phone": "+1 (958) 577-2935", 1321 | "address": "254 Stewart Street, Frizzleburg, Virgin Islands, 5988", 1322 | "about": "Non consequat adipisicing veniam officia ex. Dolor et aute tempor adipisicing sit ipsum velit proident. Voluptate commodo incididunt magna officia qui. Veniam commodo sit culpa sunt deserunt adipisicing dolor quis voluptate eiusmod. Laboris commodo esse dolor esse. Nulla aute et sunt reprehenderit non.\r\n", 1323 | "registered": "2016-04-25T02:27:54 -01:00", 1324 | "latitude": 0.327608, 1325 | "longitude": -116.654915, 1326 | "tags": [ 1327 | "pariatur", 1328 | "elit", 1329 | "deserunt", 1330 | "sint", 1331 | "Lorem", 1332 | "amet", 1333 | "proident" 1334 | ], 1335 | "friends": [ 1336 | { 1337 | "id": 0, 1338 | "name": "Jewell Day" 1339 | }, 1340 | { 1341 | "id": 1, 1342 | "name": "Fuller Trujillo" 1343 | }, 1344 | { 1345 | "id": 2, 1346 | "name": "Gracie Mercado" 1347 | } 1348 | ], 1349 | "greeting": "Hello, Montoya Pugh! You have 10 unread messages.", 1350 | "favoriteFruit": "banana" 1351 | }, 1352 | { 1353 | "_id": "5dddbc7c571eee429548b98e", 1354 | "index": 30, 1355 | "guid": "0314a145-4b3c-4730-b883-b1fa6af567ac", 1356 | "isActive": false, 1357 | "balance": "$3,565.35", 1358 | "picture": "http://placehold.it/32x32", 1359 | "age": 28, 1360 | "eyeColor": "brown", 1361 | "name": "Ferguson Compton", 1362 | "gender": "male", 1363 | "company": "CIRCUM", 1364 | "email": "fergusoncompton@circum.com", 1365 | "phone": "+1 (939) 516-3424", 1366 | "address": "459 Hyman Court, Hilltop, New Hampshire, 6768", 1367 | "about": "Occaecat proident laborum qui est eiusmod Lorem consectetur minim qui. Id exercitation sunt Lorem quis amet. Nisi pariatur tempor velit proident ex incididunt elit sunt. Non sit cupidatat nostrud irure velit consequat esse aliqua amet ut reprehenderit enim exercitation anim. Id non magna ad incididunt aliquip pariatur cupidatat eu culpa fugiat dolore dolore. Minim laborum dolore in enim occaecat dolor tempor consectetur anim sit dolor quis ipsum et.\r\n", 1368 | "registered": "2017-07-28T12:19:50 -01:00", 1369 | "latitude": 57.06262, 1370 | "longitude": 6.491944, 1371 | "tags": [ 1372 | "ipsum", 1373 | "dolor", 1374 | "laboris", 1375 | "ut", 1376 | "culpa", 1377 | "minim", 1378 | "qui" 1379 | ], 1380 | "friends": [ 1381 | { 1382 | "id": 0, 1383 | "name": "Dixie Nash" 1384 | }, 1385 | { 1386 | "id": 1, 1387 | "name": "Peck Hines" 1388 | }, 1389 | { 1390 | "id": 2, 1391 | "name": "Juanita Young" 1392 | } 1393 | ], 1394 | "greeting": "Hello, Ferguson Compton! You have 4 unread messages.", 1395 | "favoriteFruit": "apple" 1396 | }, 1397 | { 1398 | "_id": "5dddbc7cb54a1b1ae3265aa3", 1399 | "index": 31, 1400 | "guid": "fbd0435f-07cd-4f64-9f4e-78804a1b7422", 1401 | "isActive": true, 1402 | "balance": "$3,957.71", 1403 | "picture": "http://placehold.it/32x32", 1404 | "age": 29, 1405 | "eyeColor": "brown", 1406 | "name": "Lydia Horne", 1407 | "gender": "female", 1408 | "company": "BIOHAB", 1409 | "email": "lydiahorne@biohab.com", 1410 | "phone": "+1 (809) 541-3933", 1411 | "address": "287 Albany Avenue, Dana, Alaska, 6845", 1412 | "about": "Dolore qui nisi pariatur labore do ea. Proident adipisicing dolor magna quis quis laboris occaecat qui nulla enim consectetur voluptate. Eiusmod ipsum anim adipisicing nulla velit labore elit duis id sit culpa laborum magna. Magna fugiat velit sint excepteur eiusmod. Exercitation eiusmod cupidatat velit anim consequat eiusmod adipisicing irure nulla officia.\r\n", 1413 | "registered": "2014-09-21T01:47:54 -01:00", 1414 | "latitude": 87.670845, 1415 | "longitude": -63.383144, 1416 | "tags": [ 1417 | "qui", 1418 | "dolore", 1419 | "sit", 1420 | "ad", 1421 | "enim", 1422 | "nisi", 1423 | "fugiat" 1424 | ], 1425 | "friends": [ 1426 | { 1427 | "id": 0, 1428 | "name": "Jeri Vance" 1429 | }, 1430 | { 1431 | "id": 1, 1432 | "name": "Kate Bridges" 1433 | }, 1434 | { 1435 | "id": 2, 1436 | "name": "Davidson Pratt" 1437 | } 1438 | ], 1439 | "greeting": "Hello, Lydia Horne! You have 4 unread messages.", 1440 | "favoriteFruit": "banana" 1441 | }, 1442 | { 1443 | "_id": "5dddbc7c47872ad5acd9e7b6", 1444 | "index": 32, 1445 | "guid": "18349386-9d7e-4305-a92a-96e99966860d", 1446 | "isActive": true, 1447 | "balance": "$2,205.75", 1448 | "picture": "http://placehold.it/32x32", 1449 | "age": 34, 1450 | "eyeColor": "blue", 1451 | "name": "Hewitt Logan", 1452 | "gender": "male", 1453 | "company": "BYTREX", 1454 | "email": "hewittlogan@bytrex.com", 1455 | "phone": "+1 (964) 415-2830", 1456 | "address": "324 Bogart Street, Machias, Texas, 1908", 1457 | "about": "Enim incididunt adipisicing elit elit ullamco commodo enim duis. Irure velit sit commodo aliquip dolor ad cillum. Tempor incididunt excepteur cupidatat labore anim nulla. Amet adipisicing elit anim ut Lorem non sunt fugiat. Dolore esse nulla commodo elit deserunt reprehenderit.\r\n", 1458 | "registered": "2019-01-04T11:46:29 -00:00", 1459 | "latitude": -35.833842, 1460 | "longitude": -46.686737, 1461 | "tags": [ 1462 | "sint", 1463 | "aliqua", 1464 | "laborum", 1465 | "quis", 1466 | "sunt", 1467 | "aute", 1468 | "consequat" 1469 | ], 1470 | "friends": [ 1471 | { 1472 | "id": 0, 1473 | "name": "Jenifer Castillo" 1474 | }, 1475 | { 1476 | "id": 1, 1477 | "name": "Lorene Conway" 1478 | }, 1479 | { 1480 | "id": 2, 1481 | "name": "Kinney Huber" 1482 | } 1483 | ], 1484 | "greeting": "Hello, Hewitt Logan! You have 9 unread messages.", 1485 | "favoriteFruit": "apple" 1486 | }, 1487 | { 1488 | "_id": "5dddbc7c6f959b755f6639a0", 1489 | "index": 33, 1490 | "guid": "288b5812-dbce-4104-a2f6-07872bbb5523", 1491 | "isActive": true, 1492 | "balance": "$1,774.91", 1493 | "picture": "http://placehold.it/32x32", 1494 | "age": 33, 1495 | "eyeColor": "green", 1496 | "name": "Hood Vaughn", 1497 | "gender": "male", 1498 | "company": "ACIUM", 1499 | "email": "hoodvaughn@acium.com", 1500 | "phone": "+1 (910) 428-2007", 1501 | "address": "299 Clark Street, Chilton, Mississippi, 9456", 1502 | "about": "Ea do in laborum amet. Nisi fugiat excepteur ipsum consectetur cillum magna. Irure velit aute ipsum amet proident aute duis ipsum reprehenderit ex. Excepteur deserunt culpa est est sint. Sint cillum culpa in officia commodo labore aliquip velit cupidatat nostrud amet duis dolor. Lorem dolor exercitation exercitation enim deserunt qui nulla consequat eiusmod exercitation cupidatat. Dolor ad cillum exercitation qui consectetur sunt culpa reprehenderit ullamco adipisicing culpa.\r\n", 1503 | "registered": "2015-11-27T03:24:16 -00:00", 1504 | "latitude": -32.535089, 1505 | "longitude": -73.833828, 1506 | "tags": [ 1507 | "voluptate", 1508 | "labore", 1509 | "qui", 1510 | "commodo", 1511 | "ipsum", 1512 | "veniam", 1513 | "est" 1514 | ], 1515 | "friends": [ 1516 | { 1517 | "id": 0, 1518 | "name": "Clara Warner" 1519 | }, 1520 | { 1521 | "id": 1, 1522 | "name": "Knapp Bailey" 1523 | }, 1524 | { 1525 | "id": 2, 1526 | "name": "Jane Stanton" 1527 | } 1528 | ], 1529 | "greeting": "Hello, Hood Vaughn! You have 2 unread messages.", 1530 | "favoriteFruit": "strawberry" 1531 | }, 1532 | { 1533 | "_id": "5dddbc7cfb46969c537ea3c8", 1534 | "index": 34, 1535 | "guid": "5b260325-be85-41a4-aa3e-add489f446ae", 1536 | "isActive": false, 1537 | "balance": "$2,992.08", 1538 | "picture": "http://placehold.it/32x32", 1539 | "age": 29, 1540 | "eyeColor": "blue", 1541 | "name": "Cline Mcmillan", 1542 | "gender": "male", 1543 | "company": "CANDECOR", 1544 | "email": "clinemcmillan@candecor.com", 1545 | "phone": "+1 (971) 550-2083", 1546 | "address": "285 Karweg Place, Chloride, Maryland, 9147", 1547 | "about": "Ut enim duis qui velit voluptate esse fugiat. Ipsum qui eiusmod exercitation cillum in. Ad laborum incididunt exercitation Lorem proident voluptate. Qui non nostrud ipsum qui deserunt qui minim veniam excepteur. Eu proident aliqua non voluptate. Anim fugiat ex incididunt cillum eu do commodo voluptate culpa nulla nostrud. Dolore ad non laboris aliquip labore ea laboris qui excepteur ipsum.\r\n", 1548 | "registered": "2014-06-08T03:30:29 -01:00", 1549 | "latitude": 2.79876, 1550 | "longitude": 130.695112, 1551 | "tags": [ 1552 | "adipisicing", 1553 | "elit", 1554 | "duis", 1555 | "proident", 1556 | "excepteur", 1557 | "tempor", 1558 | "nulla" 1559 | ], 1560 | "friends": [ 1561 | { 1562 | "id": 0, 1563 | "name": "Tyson Cantu" 1564 | }, 1565 | { 1566 | "id": 1, 1567 | "name": "Angelina King" 1568 | }, 1569 | { 1570 | "id": 2, 1571 | "name": "Marquez Barton" 1572 | } 1573 | ], 1574 | "greeting": "Hello, Cline Mcmillan! You have 9 unread messages.", 1575 | "favoriteFruit": "banana" 1576 | }, 1577 | { 1578 | "_id": "5dddbc7ce21d3a659ebf0247", 1579 | "index": 35, 1580 | "guid": "561401ee-309c-476c-b5b3-993aba3071b5", 1581 | "isActive": true, 1582 | "balance": "$1,121.73", 1583 | "picture": "http://placehold.it/32x32", 1584 | "age": 40, 1585 | "eyeColor": "brown", 1586 | "name": "Dianne Barron", 1587 | "gender": "female", 1588 | "company": "TWIGGERY", 1589 | "email": "diannebarron@twiggery.com", 1590 | "phone": "+1 (972) 448-3640", 1591 | "address": "990 Berry Street, Sultana, California, 761", 1592 | "about": "Labore enim ea proident anim magna consequat proident eu voluptate aliquip nulla nisi. Cillum sit voluptate dolor esse ullamco duis in cupidatat ea laboris. Velit dolor velit culpa nisi Lorem tempor. Magna nulla do laboris commodo consequat veniam. Cupidatat est mollit aliquip nulla est irure dolor ad eiusmod ipsum.\r\n", 1593 | "registered": "2019-04-20T12:31:29 -01:00", 1594 | "latitude": -59.304962, 1595 | "longitude": 63.115831, 1596 | "tags": [ 1597 | "adipisicing", 1598 | "sint", 1599 | "ea", 1600 | "dolor", 1601 | "ea", 1602 | "non", 1603 | "elit" 1604 | ], 1605 | "friends": [ 1606 | { 1607 | "id": 0, 1608 | "name": "Benton Schneider" 1609 | }, 1610 | { 1611 | "id": 1, 1612 | "name": "Marta Michael" 1613 | }, 1614 | { 1615 | "id": 2, 1616 | "name": "Eaton Oneal" 1617 | } 1618 | ], 1619 | "greeting": "Hello, Dianne Barron! You have 4 unread messages.", 1620 | "favoriteFruit": "strawberry" 1621 | }, 1622 | { 1623 | "_id": "5dddbc7c686443d8f65c1988", 1624 | "index": 36, 1625 | "guid": "038b2071-df39-4f50-9ec5-6d94aceb955f", 1626 | "isActive": true, 1627 | "balance": "$1,653.70", 1628 | "picture": "http://placehold.it/32x32", 1629 | "age": 22, 1630 | "eyeColor": "brown", 1631 | "name": "Wade Lynch", 1632 | "gender": "male", 1633 | "company": "SUREPLEX", 1634 | "email": "wadelynch@sureplex.com", 1635 | "phone": "+1 (853) 578-2064", 1636 | "address": "793 Friel Place, Haena, Wyoming, 7594", 1637 | "about": "Id labore nulla occaecat eiusmod ad fugiat eu et esse voluptate enim labore ut. Est in occaecat amet nulla nulla aliquip ut excepteur cillum nisi id Lorem enim. Ipsum cupidatat ex dolor elit incididunt sint nulla Lorem magna est consectetur aliqua. Eu non tempor velit deserunt labore. Nisi id officia veniam tempor sunt exercitation.\r\n", 1638 | "registered": "2019-05-04T09:41:50 -01:00", 1639 | "latitude": 55.857456, 1640 | "longitude": 18.66363, 1641 | "tags": [ 1642 | "nulla", 1643 | "duis", 1644 | "non", 1645 | "ex", 1646 | "reprehenderit", 1647 | "aliquip", 1648 | "ullamco" 1649 | ], 1650 | "friends": [ 1651 | { 1652 | "id": 0, 1653 | "name": "Hammond Moran" 1654 | }, 1655 | { 1656 | "id": 1, 1657 | "name": "Langley Sandoval" 1658 | }, 1659 | { 1660 | "id": 2, 1661 | "name": "Dora Hess" 1662 | } 1663 | ], 1664 | "greeting": "Hello, Wade Lynch! You have 9 unread messages.", 1665 | "favoriteFruit": "strawberry" 1666 | }, 1667 | { 1668 | "_id": "5dddbc7c354ab1b8e73652c1", 1669 | "index": 37, 1670 | "guid": "10d4ced1-ca6b-4aa7-8d0e-d1d3d66705c6", 1671 | "isActive": true, 1672 | "balance": "$1,499.05", 1673 | "picture": "http://placehold.it/32x32", 1674 | "age": 34, 1675 | "eyeColor": "brown", 1676 | "name": "Luisa Webster", 1677 | "gender": "female", 1678 | "company": "CEDWARD", 1679 | "email": "luisawebster@cedward.com", 1680 | "phone": "+1 (909) 467-2208", 1681 | "address": "592 Ide Court, Sunriver, Louisiana, 4411", 1682 | "about": "Excepteur consequat occaecat ipsum cupidatat eu cupidatat ea consectetur consequat tempor minim. Et tempor deserunt minim officia deserunt officia nulla ex ad deserunt aliqua fugiat ipsum deserunt. Adipisicing id qui ex consectetur incididunt anim in eu deserunt. Irure dolor mollit dolor tempor ea nulla sit. Pariatur dolor exercitation velit ipsum et magna fugiat Lorem culpa excepteur nisi. Tempor do excepteur laborum ea fugiat dolor amet nostrud adipisicing culpa tempor ad.\r\n", 1683 | "registered": "2018-06-15T08:44:15 -01:00", 1684 | "latitude": -63.765884, 1685 | "longitude": -164.663819, 1686 | "tags": [ 1687 | "ea", 1688 | "commodo", 1689 | "voluptate", 1690 | "quis", 1691 | "laboris", 1692 | "ex", 1693 | "laboris" 1694 | ], 1695 | "friends": [ 1696 | { 1697 | "id": 0, 1698 | "name": "Saundra Bartlett" 1699 | }, 1700 | { 1701 | "id": 1, 1702 | "name": "Haley Knapp" 1703 | }, 1704 | { 1705 | "id": 2, 1706 | "name": "Gay Dale" 1707 | } 1708 | ], 1709 | "greeting": "Hello, Luisa Webster! You have 3 unread messages.", 1710 | "favoriteFruit": "banana" 1711 | }, 1712 | { 1713 | "_id": "5dddbc7c952e155fa5700fe2", 1714 | "index": 38, 1715 | "guid": "4038fa87-d79e-40b6-8d1a-995e4f964008", 1716 | "isActive": true, 1717 | "balance": "$2,102.54", 1718 | "picture": "http://placehold.it/32x32", 1719 | "age": 31, 1720 | "eyeColor": "green", 1721 | "name": "Terri Mercer", 1722 | "gender": "female", 1723 | "company": "PUSHCART", 1724 | "email": "terrimercer@pushcart.com", 1725 | "phone": "+1 (833) 560-2466", 1726 | "address": "330 Bowery Street, Draper, South Carolina, 7560", 1727 | "about": "Et ut in cupidatat eu cupidatat. Mollit do incididunt id ullamco ad est. Duis aute nulla fugiat exercitation. Sit proident culpa laboris mollit exercitation enim proident ullamco velit sint consectetur anim id adipisicing. Id et cupidatat elit officia do excepteur. Nisi exercitation nulla esse aliquip. Ad irure veniam est commodo ullamco ut.\r\n", 1728 | "registered": "2015-01-31T10:58:53 -00:00", 1729 | "latitude": -88.594943, 1730 | "longitude": -118.492837, 1731 | "tags": [ 1732 | "minim", 1733 | "ex", 1734 | "reprehenderit", 1735 | "in", 1736 | "laborum", 1737 | "ea", 1738 | "esse" 1739 | ], 1740 | "friends": [ 1741 | { 1742 | "id": 0, 1743 | "name": "Frederick Parks" 1744 | }, 1745 | { 1746 | "id": 1, 1747 | "name": "Molly Clemons" 1748 | }, 1749 | { 1750 | "id": 2, 1751 | "name": "Rosella Norman" 1752 | } 1753 | ], 1754 | "greeting": "Hello, Terri Mercer! You have 10 unread messages.", 1755 | "favoriteFruit": "apple" 1756 | }, 1757 | { 1758 | "_id": "5dddbc7cbb173733abbbe10f", 1759 | "index": 39, 1760 | "guid": "2916700d-8752-4c64-8d2c-1f82c8e3bf8e", 1761 | "isActive": true, 1762 | "balance": "$3,044.05", 1763 | "picture": "http://placehold.it/32x32", 1764 | "age": 35, 1765 | "eyeColor": "brown", 1766 | "name": "Adele Ross", 1767 | "gender": "female", 1768 | "company": "FRANSCENE", 1769 | "email": "adeleross@franscene.com", 1770 | "phone": "+1 (881) 483-3412", 1771 | "address": "995 River Street, Blue, Ohio, 9435", 1772 | "about": "Proident duis eu adipisicing consectetur nulla veniam ad in. Nulla magna dolore aliquip fugiat do non. Exercitation et elit veniam magna veniam deserunt incididunt occaecat amet deserunt nostrud proident pariatur. Eu nisi dolore tempor ex.\r\n", 1773 | "registered": "2018-12-23T12:00:07 -00:00", 1774 | "latitude": -76.5717, 1775 | "longitude": 81.68831, 1776 | "tags": [ 1777 | "veniam", 1778 | "mollit", 1779 | "et", 1780 | "cillum", 1781 | "occaecat", 1782 | "tempor", 1783 | "eu" 1784 | ], 1785 | "friends": [ 1786 | { 1787 | "id": 0, 1788 | "name": "Marietta Lynn" 1789 | }, 1790 | { 1791 | "id": 1, 1792 | "name": "Kristine Combs" 1793 | }, 1794 | { 1795 | "id": 2, 1796 | "name": "Underwood Kinney" 1797 | } 1798 | ], 1799 | "greeting": "Hello, Adele Ross! You have 10 unread messages.", 1800 | "favoriteFruit": "apple" 1801 | }, 1802 | { 1803 | "_id": "5dddbc7ca56daf5dd71facd6", 1804 | "index": 40, 1805 | "guid": "e4e2fa4e-a07a-4e97-a56a-5de94f53d922", 1806 | "isActive": true, 1807 | "balance": "$3,310.61", 1808 | "picture": "http://placehold.it/32x32", 1809 | "age": 35, 1810 | "eyeColor": "brown", 1811 | "name": "Briggs Hardin", 1812 | "gender": "male", 1813 | "company": "OCEANICA", 1814 | "email": "briggshardin@oceanica.com", 1815 | "phone": "+1 (952) 480-2567", 1816 | "address": "679 Polhemus Place, Gibsonia, North Carolina, 7664", 1817 | "about": "Aute magna non voluptate magna voluptate nulla officia reprehenderit velit exercitation aute esse est tempor. Velit eu Lorem velit nostrud consectetur eu. Sit adipisicing nostrud laboris fugiat eu ex qui laboris non est laboris. Sit ad nostrud pariatur duis excepteur adipisicing officia.\r\n", 1818 | "registered": "2018-10-31T04:26:21 -00:00", 1819 | "latitude": 25.204394, 1820 | "longitude": 41.770074, 1821 | "tags": [ 1822 | "reprehenderit", 1823 | "dolor", 1824 | "fugiat", 1825 | "ea", 1826 | "amet", 1827 | "mollit", 1828 | "et" 1829 | ], 1830 | "friends": [ 1831 | { 1832 | "id": 0, 1833 | "name": "Matilda Sweet" 1834 | }, 1835 | { 1836 | "id": 1, 1837 | "name": "Wright Neal" 1838 | }, 1839 | { 1840 | "id": 2, 1841 | "name": "Lila Travis" 1842 | } 1843 | ], 1844 | "greeting": "Hello, Briggs Hardin! You have 6 unread messages.", 1845 | "favoriteFruit": "apple" 1846 | }, 1847 | { 1848 | "_id": "5dddbc7c34d7fdd122349fd4", 1849 | "index": 41, 1850 | "guid": "d29f0dce-372f-4d26-b4a5-08446c71970f", 1851 | "isActive": false, 1852 | "balance": "$2,980.40", 1853 | "picture": "http://placehold.it/32x32", 1854 | "age": 24, 1855 | "eyeColor": "green", 1856 | "name": "Byers Ward", 1857 | "gender": "male", 1858 | "company": "MINGA", 1859 | "email": "byersward@minga.com", 1860 | "phone": "+1 (890) 437-3497", 1861 | "address": "426 Montague Terrace, Jeff, Guam, 8663", 1862 | "about": "Nisi voluptate ullamco sunt dolor dolor aliqua adipisicing. Culpa cupidatat proident est non. Aute sit consequat fugiat est nulla. Dolor consequat et tempor cupidatat ea eiusmod commodo do officia incididunt occaecat culpa tempor. Minim quis pariatur enim minim.\r\n", 1863 | "registered": "2016-10-15T10:22:39 -01:00", 1864 | "latitude": 54.856047, 1865 | "longitude": 107.725244, 1866 | "tags": [ 1867 | "amet", 1868 | "est", 1869 | "laboris", 1870 | "ad", 1871 | "ut", 1872 | "occaecat", 1873 | "mollit" 1874 | ], 1875 | "friends": [ 1876 | { 1877 | "id": 0, 1878 | "name": "Bernadette Fry" 1879 | }, 1880 | { 1881 | "id": 1, 1882 | "name": "Saunders Whitfield" 1883 | }, 1884 | { 1885 | "id": 2, 1886 | "name": "Mcknight Snyder" 1887 | } 1888 | ], 1889 | "greeting": "Hello, Byers Ward! You have 8 unread messages.", 1890 | "favoriteFruit": "apple" 1891 | }, 1892 | { 1893 | "_id": "5dddbc7c86e746ada989da99", 1894 | "index": 42, 1895 | "guid": "be8f3f0e-e14d-42d6-8d03-0006753d31e0", 1896 | "isActive": true, 1897 | "balance": "$2,300.75", 1898 | "picture": "http://placehold.it/32x32", 1899 | "age": 23, 1900 | "eyeColor": "green", 1901 | "name": "Tran Pennington", 1902 | "gender": "male", 1903 | "company": "ZOLAR", 1904 | "email": "tranpennington@zolar.com", 1905 | "phone": "+1 (971) 433-2129", 1906 | "address": "453 Martense Street, Garnet, South Dakota, 3533", 1907 | "about": "Anim commodo aute in ipsum esse incididunt mollit ea ea ut occaecat in. Fugiat commodo ut sint exercitation anim sint et. Ut mollit est consectetur laborum eiusmod labore ex. Enim exercitation dolore aliquip et eiusmod incididunt dolore incididunt sunt sunt anim eu mollit mollit.\r\n", 1908 | "registered": "2018-02-09T09:34:00 -00:00", 1909 | "latitude": -85.047729, 1910 | "longitude": -97.33869, 1911 | "tags": [ 1912 | "aliquip", 1913 | "eu", 1914 | "et", 1915 | "irure", 1916 | "in", 1917 | "amet", 1918 | "exercitation" 1919 | ], 1920 | "friends": [ 1921 | { 1922 | "id": 0, 1923 | "name": "Effie Houston" 1924 | }, 1925 | { 1926 | "id": 1, 1927 | "name": "Earlene Gaines" 1928 | }, 1929 | { 1930 | "id": 2, 1931 | "name": "Macias Smith" 1932 | } 1933 | ], 1934 | "greeting": "Hello, Tran Pennington! You have 3 unread messages.", 1935 | "favoriteFruit": "strawberry" 1936 | }, 1937 | { 1938 | "_id": "5dddbc7c2fc7563d02acc87c", 1939 | "index": 43, 1940 | "guid": "43d61ca8-d52b-4588-a7a2-5459e8bd66c6", 1941 | "isActive": false, 1942 | "balance": "$2,456.22", 1943 | "picture": "http://placehold.it/32x32", 1944 | "age": 24, 1945 | "eyeColor": "green", 1946 | "name": "Marisa Henry", 1947 | "gender": "female", 1948 | "company": "POLARIA", 1949 | "email": "marisahenry@polaria.com", 1950 | "phone": "+1 (972) 507-3849", 1951 | "address": "840 Euclid Avenue, Monument, New Mexico, 3279", 1952 | "about": "Fugiat aliqua ut occaecat non duis quis Lorem id consequat ullamco in pariatur laboris. Sit fugiat duis reprehenderit quis consectetur. Eu irure enim aliquip non velit deserunt quis. Incididunt cillum nisi cillum mollit laboris elit veniam. Velit aute nostrud laboris elit pariatur mollit quis adipisicing.\r\n", 1953 | "registered": "2016-09-06T11:22:13 -01:00", 1954 | "latitude": -78.10994, 1955 | "longitude": -112.316205, 1956 | "tags": [ 1957 | "cillum", 1958 | "culpa", 1959 | "incididunt", 1960 | "cillum", 1961 | "est", 1962 | "do", 1963 | "consequat" 1964 | ], 1965 | "friends": [ 1966 | { 1967 | "id": 0, 1968 | "name": "Elsa Olsen" 1969 | }, 1970 | { 1971 | "id": 1, 1972 | "name": "Arlene Mccormick" 1973 | }, 1974 | { 1975 | "id": 2, 1976 | "name": "Horne House" 1977 | } 1978 | ], 1979 | "greeting": "Hello, Marisa Henry! You have 5 unread messages.", 1980 | "favoriteFruit": "banana" 1981 | }, 1982 | { 1983 | "_id": "5dddbc7c2a77080a8db4f3f6", 1984 | "index": 44, 1985 | "guid": "53cb1cd0-8a6d-4d48-a043-6691b6f0a4a2", 1986 | "isActive": true, 1987 | "balance": "$2,812.90", 1988 | "picture": "http://placehold.it/32x32", 1989 | "age": 34, 1990 | "eyeColor": "brown", 1991 | "name": "Keith Frank", 1992 | "gender": "male", 1993 | "company": "AQUAMATE", 1994 | "email": "keithfrank@aquamate.com", 1995 | "phone": "+1 (913) 597-3341", 1996 | "address": "622 Farragut Place, Olney, Arizona, 2005", 1997 | "about": "Mollit deserunt enim et commodo Lorem qui excepteur ex officia dolor incididunt qui. Ad nulla et et ut excepteur fugiat dolor. Veniam culpa magna exercitation commodo consequat cillum aute. Et cillum velit proident ea nulla ex exercitation reprehenderit consectetur aute ipsum et magna. Dolor incididunt anim cupidatat commodo ipsum ipsum. Consectetur ex irure est aute qui nisi mollit fugiat.\r\n", 1998 | "registered": "2016-02-06T12:59:23 -00:00", 1999 | "latitude": 86.860378, 2000 | "longitude": -8.091903, 2001 | "tags": [ 2002 | "in", 2003 | "adipisicing", 2004 | "nulla", 2005 | "labore", 2006 | "elit", 2007 | "voluptate", 2008 | "deserunt" 2009 | ], 2010 | "friends": [ 2011 | { 2012 | "id": 0, 2013 | "name": "Rodriquez Keller" 2014 | }, 2015 | { 2016 | "id": 1, 2017 | "name": "Cara Farmer" 2018 | }, 2019 | { 2020 | "id": 2, 2021 | "name": "Hardy Graham" 2022 | } 2023 | ], 2024 | "greeting": "Hello, Keith Frank! You have 10 unread messages.", 2025 | "favoriteFruit": "apple" 2026 | }, 2027 | { 2028 | "_id": "5dddbc7cf00234e3af905ef5", 2029 | "index": 45, 2030 | "guid": "5d7a36eb-a052-452a-8c90-f2d843ce1c5a", 2031 | "isActive": true, 2032 | "balance": "$1,306.36", 2033 | "picture": "http://placehold.it/32x32", 2034 | "age": 39, 2035 | "eyeColor": "blue", 2036 | "name": "Brenda Morgan", 2037 | "gender": "female", 2038 | "company": "FUELTON", 2039 | "email": "brendamorgan@fuelton.com", 2040 | "phone": "+1 (912) 490-2582", 2041 | "address": "162 Highland Boulevard, Vienna, Delaware, 3680", 2042 | "about": "Id et mollit officia officia. Labore ex id laboris minim et duis ullamco id laborum. Fugiat ex ea minim dolore consectetur enim ad sint reprehenderit fugiat consectetur. Officia ullamco dolore non aliquip in sunt enim. Mollit in tempor officia occaecat dolore cillum anim officia pariatur laboris dolor veniam. Laborum dolor voluptate non tempor duis est aute veniam.\r\n", 2043 | "registered": "2017-02-24T05:09:26 -00:00", 2044 | "latitude": 52.700686, 2045 | "longitude": -167.247858, 2046 | "tags": [ 2047 | "deserunt", 2048 | "mollit", 2049 | "aliqua", 2050 | "eu", 2051 | "nulla", 2052 | "enim", 2053 | "deserunt" 2054 | ], 2055 | "friends": [ 2056 | { 2057 | "id": 0, 2058 | "name": "Stephanie Hancock" 2059 | }, 2060 | { 2061 | "id": 1, 2062 | "name": "Velasquez Cain" 2063 | }, 2064 | { 2065 | "id": 2, 2066 | "name": "Regina Boone" 2067 | } 2068 | ], 2069 | "greeting": "Hello, Brenda Morgan! You have 2 unread messages.", 2070 | "favoriteFruit": "apple" 2071 | }, 2072 | { 2073 | "_id": "5dddbc7c0adfb1ade60b0d2d", 2074 | "index": 46, 2075 | "guid": "20b4e4e6-3e1a-40c8-b028-596179db047f", 2076 | "isActive": true, 2077 | "balance": "$2,859.87", 2078 | "picture": "http://placehold.it/32x32", 2079 | "age": 20, 2080 | "eyeColor": "blue", 2081 | "name": "Rae Cochran", 2082 | "gender": "female", 2083 | "company": "ANIMALIA", 2084 | "email": "raecochran@animalia.com", 2085 | "phone": "+1 (869) 508-3244", 2086 | "address": "414 Schroeders Avenue, Dupuyer, Utah, 6311", 2087 | "about": "Lorem enim ea Lorem dolore fugiat dolor amet ut eu sit ex. Adipisicing ipsum officia Lorem anim officia voluptate dolore est Lorem voluptate. Anim aute in aute reprehenderit reprehenderit magna exercitation. Officia cupidatat nulla reprehenderit voluptate culpa anim fugiat nisi exercitation anim dolore cillum enim qui.\r\n", 2088 | "registered": "2014-10-15T06:51:04 -01:00", 2089 | "latitude": 9.489484, 2090 | "longitude": -60.445117, 2091 | "tags": [ 2092 | "proident", 2093 | "cillum", 2094 | "laboris", 2095 | "esse", 2096 | "dolor", 2097 | "reprehenderit", 2098 | "aliqua" 2099 | ], 2100 | "friends": [ 2101 | { 2102 | "id": 0, 2103 | "name": "Ebony Chapman" 2104 | }, 2105 | { 2106 | "id": 1, 2107 | "name": "Estes Lara" 2108 | }, 2109 | { 2110 | "id": 2, 2111 | "name": "Jasmine Perez" 2112 | } 2113 | ], 2114 | "greeting": "Hello, Rae Cochran! You have 4 unread messages.", 2115 | "favoriteFruit": "strawberry" 2116 | }, 2117 | { 2118 | "_id": "5dddbc7c46769ee8e8647329", 2119 | "index": 47, 2120 | "guid": "17f2da56-6bd3-448b-ac90-94130c2a8f3c", 2121 | "isActive": true, 2122 | "balance": "$3,939.98", 2123 | "picture": "http://placehold.it/32x32", 2124 | "age": 20, 2125 | "eyeColor": "green", 2126 | "name": "Figueroa Banks", 2127 | "gender": "male", 2128 | "company": "BILLMED", 2129 | "email": "figueroabanks@billmed.com", 2130 | "phone": "+1 (916) 448-2320", 2131 | "address": "908 Hausman Street, Rew, Kentucky, 502", 2132 | "about": "Consequat in quis laborum et ullamco irure occaecat Lorem esse eu ad non nulla mollit. Consequat dolor consequat proident adipisicing. Dolor magna ad et reprehenderit occaecat irure magna. Eu incididunt est ut aliqua tempor eu elit id laborum do amet magna adipisicing fugiat. Est elit non qui sint eu consectetur duis nulla eu consectetur nulla exercitation quis. Ad sint id officia ipsum non sit cillum officia dolor reprehenderit quis aliquip aute. Fugiat do fugiat fugiat proident tempor occaecat mollit incididunt est nostrud duis.\r\n", 2133 | "registered": "2017-08-02T10:35:30 -01:00", 2134 | "latitude": 3.0128, 2135 | "longitude": -15.215475, 2136 | "tags": [ 2137 | "et", 2138 | "mollit", 2139 | "fugiat", 2140 | "veniam", 2141 | "dolor", 2142 | "aute", 2143 | "anim" 2144 | ], 2145 | "friends": [ 2146 | { 2147 | "id": 0, 2148 | "name": "Chen Rodriquez" 2149 | }, 2150 | { 2151 | "id": 1, 2152 | "name": "Pate Moore" 2153 | }, 2154 | { 2155 | "id": 2, 2156 | "name": "Mcconnell Anthony" 2157 | } 2158 | ], 2159 | "greeting": "Hello, Figueroa Banks! You have 9 unread messages.", 2160 | "favoriteFruit": "strawberry" 2161 | }, 2162 | { 2163 | "_id": "5dddbc7c4dea85db3e1da650", 2164 | "index": 48, 2165 | "guid": "9afae274-80b5-4554-a6e5-757f15121571", 2166 | "isActive": false, 2167 | "balance": "$1,443.83", 2168 | "picture": "http://placehold.it/32x32", 2169 | "age": 25, 2170 | "eyeColor": "blue", 2171 | "name": "Wise Hickman", 2172 | "gender": "male", 2173 | "company": "ANARCO", 2174 | "email": "wisehickman@anarco.com", 2175 | "phone": "+1 (816) 413-2351", 2176 | "address": "531 Prospect Street, Wedgewood, Georgia, 666", 2177 | "about": "Culpa occaecat Lorem aliquip aliquip cillum. Reprehenderit ea incididunt velit ex nostrud pariatur magna amet. Occaecat sunt eiusmod id aliqua do occaecat qui amet culpa duis ea adipisicing dolore ullamco. Sunt nostrud Lorem ut id dolor. Velit ullamco elit labore fugiat do minim sunt mollit exercitation fugiat. Fugiat qui reprehenderit velit excepteur commodo elit mollit cupidatat in nulla aute.\r\n", 2178 | "registered": "2016-02-28T09:18:18 -00:00", 2179 | "latitude": 17.416729, 2180 | "longitude": -93.182362, 2181 | "tags": [ 2182 | "cupidatat", 2183 | "ut", 2184 | "voluptate", 2185 | "Lorem", 2186 | "minim", 2187 | "esse", 2188 | "deserunt" 2189 | ], 2190 | "friends": [ 2191 | { 2192 | "id": 0, 2193 | "name": "Lynne Franks" 2194 | }, 2195 | { 2196 | "id": 1, 2197 | "name": "Cassandra Paul" 2198 | }, 2199 | { 2200 | "id": 2, 2201 | "name": "Dillon Lancaster" 2202 | } 2203 | ], 2204 | "greeting": "Hello, Wise Hickman! You have 2 unread messages.", 2205 | "favoriteFruit": "apple" 2206 | }, 2207 | { 2208 | "_id": "5dddbc7c138d1bb5f9506db5", 2209 | "index": 49, 2210 | "guid": "2a32525a-b35d-4b6d-a369-bc6b27c661b2", 2211 | "isActive": false, 2212 | "balance": "$3,334.90", 2213 | "picture": "http://placehold.it/32x32", 2214 | "age": 27, 2215 | "eyeColor": "blue", 2216 | "name": "Aline Mack", 2217 | "gender": "female", 2218 | "company": "LEXICONDO", 2219 | "email": "alinemack@lexicondo.com", 2220 | "phone": "+1 (940) 502-3754", 2221 | "address": "959 Post Court, Brogan, Colorado, 2750", 2222 | "about": "Sint amet in pariatur tempor qui anim aliquip et Lorem id duis. Culpa duis ad cillum dolor labore laborum adipisicing sint veniam enim eiusmod adipisicing. Enim sunt voluptate sunt voluptate sunt nulla. Aute velit cupidatat dolore tempor labore non.\r\n", 2223 | "registered": "2016-08-27T10:02:54 -01:00", 2224 | "latitude": 7.241611, 2225 | "longitude": -20.610877, 2226 | "tags": [ 2227 | "exercitation", 2228 | "eiusmod", 2229 | "tempor", 2230 | "est", 2231 | "cupidatat", 2232 | "adipisicing", 2233 | "ipsum" 2234 | ], 2235 | "friends": [ 2236 | { 2237 | "id": 0, 2238 | "name": "Sheppard Marshall" 2239 | }, 2240 | { 2241 | "id": 1, 2242 | "name": "Sabrina Perkins" 2243 | }, 2244 | { 2245 | "id": 2, 2246 | "name": "Elsie Merritt" 2247 | } 2248 | ], 2249 | "greeting": "Hello, Aline Mack! You have 4 unread messages.", 2250 | "favoriteFruit": "apple" 2251 | }, 2252 | { 2253 | "_id": "5dddbc7c2ed1e6a2fc0473e6", 2254 | "index": 50, 2255 | "guid": "1edabd49-ed93-49a2-b0f9-9ef0556fde10", 2256 | "isActive": false, 2257 | "balance": "$2,250.92", 2258 | "picture": "http://placehold.it/32x32", 2259 | "age": 26, 2260 | "eyeColor": "blue", 2261 | "name": "Jefferson Brewer", 2262 | "gender": "male", 2263 | "company": "ATGEN", 2264 | "email": "jeffersonbrewer@atgen.com", 2265 | "phone": "+1 (935) 491-2480", 2266 | "address": "533 Menahan Street, Alamo, Oregon, 5369", 2267 | "about": "Exercitation occaecat incididunt non eiusmod consectetur eu. Ex sit labore exercitation proident Lorem do velit voluptate est esse adipisicing consequat. Pariatur minim aute duis ullamco culpa elit cillum non. Sit proident aliqua quis amet irure sint est elit aliquip ipsum cillum aliqua. Occaecat cupidatat consectetur tempor aliquip aute Lorem. Officia amet est esse ut. Dolore exercitation reprehenderit reprehenderit eu minim fugiat excepteur consequat aute voluptate ut.\r\n", 2268 | "registered": "2018-08-23T02:35:10 -01:00", 2269 | "latitude": 67.47291, 2270 | "longitude": 122.337652, 2271 | "tags": [ 2272 | "id", 2273 | "nulla", 2274 | "duis", 2275 | "esse", 2276 | "voluptate", 2277 | "non", 2278 | "aliquip" 2279 | ], 2280 | "friends": [ 2281 | { 2282 | "id": 0, 2283 | "name": "Owens Grant" 2284 | }, 2285 | { 2286 | "id": 1, 2287 | "name": "Tammie Mcfarland" 2288 | }, 2289 | { 2290 | "id": 2, 2291 | "name": "Savage Patrick" 2292 | } 2293 | ], 2294 | "greeting": "Hello, Jefferson Brewer! You have 3 unread messages.", 2295 | "favoriteFruit": "banana" 2296 | }, 2297 | { 2298 | "_id": "5dddbc7ceed204a369d9ee11", 2299 | "index": 51, 2300 | "guid": "79ede085-06ab-46c8-b307-6ef84419c862", 2301 | "isActive": true, 2302 | "balance": "$1,320.34", 2303 | "picture": "http://placehold.it/32x32", 2304 | "age": 21, 2305 | "eyeColor": "blue", 2306 | "name": "Williamson Gross", 2307 | "gender": "male", 2308 | "company": "OTHERWAY", 2309 | "email": "williamsongross@otherway.com", 2310 | "phone": "+1 (826) 547-2856", 2311 | "address": "994 Whitney Avenue, Chemung, Wisconsin, 5658", 2312 | "about": "Duis elit sunt ad ullamco magna aliqua in ex Lorem tempor esse adipisicing laboris fugiat. Do ad non dolore irure ex eiusmod commodo commodo ea. Velit ut sunt in cupidatat ut ex laborum adipisicing laborum. Proident nisi sit exercitation ullamco tempor anim aliquip reprehenderit velit et. Tempor adipisicing nulla eu sunt sit nostrud sunt proident velit consectetur.\r\n", 2313 | "registered": "2017-07-17T09:02:14 -01:00", 2314 | "latitude": 52.586309, 2315 | "longitude": 85.31855, 2316 | "tags": [ 2317 | "elit", 2318 | "et", 2319 | "veniam", 2320 | "ex", 2321 | "in", 2322 | "nostrud", 2323 | "sint" 2324 | ], 2325 | "friends": [ 2326 | { 2327 | "id": 0, 2328 | "name": "Rena Grimes" 2329 | }, 2330 | { 2331 | "id": 1, 2332 | "name": "Cote Harmon" 2333 | }, 2334 | { 2335 | "id": 2, 2336 | "name": "Daugherty Lindsay" 2337 | } 2338 | ], 2339 | "greeting": "Hello, Williamson Gross! You have 6 unread messages.", 2340 | "favoriteFruit": "banana" 2341 | }, 2342 | { 2343 | "_id": "5dddbc7c6c1bfb10fb0699dd", 2344 | "index": 52, 2345 | "guid": "9f8664a6-a312-4c07-b3cc-f70799fcd638", 2346 | "isActive": true, 2347 | "balance": "$1,530.76", 2348 | "picture": "http://placehold.it/32x32", 2349 | "age": 23, 2350 | "eyeColor": "green", 2351 | "name": "Hayes Mathis", 2352 | "gender": "male", 2353 | "company": "QUALITERN", 2354 | "email": "hayesmathis@qualitern.com", 2355 | "phone": "+1 (877) 550-3917", 2356 | "address": "391 Stillwell Avenue, Starks, Alabama, 4136", 2357 | "about": "Culpa nisi officia occaecat deserunt ex incididunt exercitation voluptate anim nisi incididunt consequat commodo. Cillum ut adipisicing minim nostrud velit aliqua anim et. Aute ad voluptate nostrud ex mollit irure ipsum adipisicing laborum culpa elit. Cillum dolor minim ea voluptate pariatur pariatur. Sit amet sit elit proident veniam in sint in pariatur adipisicing. Excepteur consectetur velit veniam pariatur exercitation sunt velit do proident ullamco aliquip ullamco. Eu laboris nisi mollit aute deserunt ad incididunt labore ullamco cillum irure cupidatat consectetur.\r\n", 2358 | "registered": "2019-01-15T03:16:45 -00:00", 2359 | "latitude": -85.317814, 2360 | "longitude": 5.449124, 2361 | "tags": [ 2362 | "officia", 2363 | "velit", 2364 | "anim", 2365 | "aliquip", 2366 | "tempor", 2367 | "elit", 2368 | "officia" 2369 | ], 2370 | "friends": [ 2371 | { 2372 | "id": 0, 2373 | "name": "Lesa Dodson" 2374 | }, 2375 | { 2376 | "id": 1, 2377 | "name": "Jayne Freeman" 2378 | }, 2379 | { 2380 | "id": 2, 2381 | "name": "Carolina Hardy" 2382 | } 2383 | ], 2384 | "greeting": "Hello, Hayes Mathis! You have 8 unread messages.", 2385 | "favoriteFruit": "strawberry" 2386 | }, 2387 | { 2388 | "_id": "5dddbc7c39947267dd3fef7e", 2389 | "index": 53, 2390 | "guid": "134cb34f-2ca8-43fc-acd8-3118edf63f68", 2391 | "isActive": true, 2392 | "balance": "$3,668.00", 2393 | "picture": "http://placehold.it/32x32", 2394 | "age": 31, 2395 | "eyeColor": "blue", 2396 | "name": "Cooke Caldwell", 2397 | "gender": "male", 2398 | "company": "QUORDATE", 2399 | "email": "cookecaldwell@quordate.com", 2400 | "phone": "+1 (849) 531-2484", 2401 | "address": "252 Monument Walk, Iberia, Rhode Island, 7367", 2402 | "about": "Aliquip culpa amet veniam nulla deserunt commodo. Officia non ex tempor nulla officia amet nulla ullamco laborum tempor. Ad est anim enim elit est eiusmod nisi tempor tempor irure. Cillum in ipsum aliqua voluptate nostrud.\r\n", 2403 | "registered": "2014-11-08T05:32:39 -00:00", 2404 | "latitude": 63.889392, 2405 | "longitude": 14.925735, 2406 | "tags": [ 2407 | "laboris", 2408 | "et", 2409 | "aute", 2410 | "nulla", 2411 | "non", 2412 | "sunt", 2413 | "eiusmod" 2414 | ], 2415 | "friends": [ 2416 | { 2417 | "id": 0, 2418 | "name": "Brock Cobb" 2419 | }, 2420 | { 2421 | "id": 1, 2422 | "name": "Bessie Cash" 2423 | }, 2424 | { 2425 | "id": 2, 2426 | "name": "Battle Clements" 2427 | } 2428 | ], 2429 | "greeting": "Hello, Cooke Caldwell! You have 4 unread messages.", 2430 | "favoriteFruit": "banana" 2431 | }, 2432 | { 2433 | "_id": "5dddbc7c58c934a1ccb21ff9", 2434 | "index": 54, 2435 | "guid": "cce3e6a1-bd66-40d2-9291-d2c83f757e3c", 2436 | "isActive": true, 2437 | "balance": "$3,376.25", 2438 | "picture": "http://placehold.it/32x32", 2439 | "age": 22, 2440 | "eyeColor": "brown", 2441 | "name": "Nixon Campbell", 2442 | "gender": "male", 2443 | "company": "OLYMPIX", 2444 | "email": "nixoncampbell@olympix.com", 2445 | "phone": "+1 (979) 405-2099", 2446 | "address": "485 Himrod Street, Felt, Idaho, 4399", 2447 | "about": "Aliqua ex officia quis et et irure ullamco cupidatat aute Lorem qui. Occaecat veniam incididunt occaecat ex consequat qui excepteur. Commodo pariatur sint amet aute ea. Excepteur ea excepteur et consequat non enim sunt est sit elit duis adipisicing. Eiusmod id mollit ut id aute dolor deserunt dolor quis eiusmod magna nisi.\r\n", 2448 | "registered": "2014-08-20T02:59:20 -01:00", 2449 | "latitude": 70.993216, 2450 | "longitude": 89.850017, 2451 | "tags": [ 2452 | "mollit", 2453 | "irure", 2454 | "anim", 2455 | "deserunt", 2456 | "cupidatat", 2457 | "veniam", 2458 | "nisi" 2459 | ], 2460 | "friends": [ 2461 | { 2462 | "id": 0, 2463 | "name": "Lynch Sanford" 2464 | }, 2465 | { 2466 | "id": 1, 2467 | "name": "Lowe Hamilton" 2468 | }, 2469 | { 2470 | "id": 2, 2471 | "name": "Helene Nielsen" 2472 | } 2473 | ], 2474 | "greeting": "Hello, Nixon Campbell! You have 5 unread messages.", 2475 | "favoriteFruit": "strawberry" 2476 | }, 2477 | { 2478 | "_id": "5dddbc7c0e599618f4586d0e", 2479 | "index": 55, 2480 | "guid": "43cc4636-4f22-48ea-8ef9-032cef76d6b8", 2481 | "isActive": false, 2482 | "balance": "$2,040.81", 2483 | "picture": "http://placehold.it/32x32", 2484 | "age": 25, 2485 | "eyeColor": "blue", 2486 | "name": "Jamie Shaw", 2487 | "gender": "female", 2488 | "company": "CAPSCREEN", 2489 | "email": "jamieshaw@capscreen.com", 2490 | "phone": "+1 (957) 549-2762", 2491 | "address": "668 Dwight Street, Kanauga, Federated States Of Micronesia, 9296", 2492 | "about": "Et voluptate mollit dolor occaecat. Sunt mollit esse nulla nulla ipsum. Reprehenderit amet amet dolore irure fugiat. Officia in labore labore officia excepteur.\r\n", 2493 | "registered": "2019-08-01T04:10:20 -01:00", 2494 | "latitude": -10.225246, 2495 | "longitude": 12.728164, 2496 | "tags": [ 2497 | "commodo", 2498 | "est", 2499 | "labore", 2500 | "ex", 2501 | "aute", 2502 | "occaecat", 2503 | "mollit" 2504 | ], 2505 | "friends": [ 2506 | { 2507 | "id": 0, 2508 | "name": "Bauer Barnett" 2509 | }, 2510 | { 2511 | "id": 1, 2512 | "name": "Lauri Farrell" 2513 | }, 2514 | { 2515 | "id": 2, 2516 | "name": "Cameron Hurley" 2517 | } 2518 | ], 2519 | "greeting": "Hello, Jamie Shaw! You have 9 unread messages.", 2520 | "favoriteFruit": "banana" 2521 | }, 2522 | { 2523 | "_id": "5dddbc7c75c9d9dbe2f99768", 2524 | "index": 56, 2525 | "guid": "7f72bfa8-9714-46c0-8b16-692bf9692277", 2526 | "isActive": true, 2527 | "balance": "$3,634.62", 2528 | "picture": "http://placehold.it/32x32", 2529 | "age": 39, 2530 | "eyeColor": "blue", 2531 | "name": "Lucy Bender", 2532 | "gender": "female", 2533 | "company": "SPLINX", 2534 | "email": "lucybender@splinx.com", 2535 | "phone": "+1 (990) 527-3088", 2536 | "address": "371 Garnet Street, Gilgo, Montana, 1287", 2537 | "about": "Elit cillum ullamco adipisicing labore. Reprehenderit laborum consequat aliquip elit. Nisi minim dolore cillum ullamco qui cupidatat incididunt.\r\n", 2538 | "registered": "2017-02-02T08:57:07 -00:00", 2539 | "latitude": -24.692039, 2540 | "longitude": -77.070799, 2541 | "tags": [ 2542 | "duis", 2543 | "dolor", 2544 | "elit", 2545 | "ad", 2546 | "sit", 2547 | "sunt", 2548 | "eu" 2549 | ], 2550 | "friends": [ 2551 | { 2552 | "id": 0, 2553 | "name": "Mack Head" 2554 | }, 2555 | { 2556 | "id": 1, 2557 | "name": "Avery Hayes" 2558 | }, 2559 | { 2560 | "id": 2, 2561 | "name": "Flores Trevino" 2562 | } 2563 | ], 2564 | "greeting": "Hello, Lucy Bender! You have 3 unread messages.", 2565 | "favoriteFruit": "strawberry" 2566 | }, 2567 | { 2568 | "_id": "5dddbc7c0cc629b97f775835", 2569 | "index": 57, 2570 | "guid": "ec30e97b-6960-49bc-be62-9dd64de6fe7a", 2571 | "isActive": true, 2572 | "balance": "$2,372.43", 2573 | "picture": "http://placehold.it/32x32", 2574 | "age": 21, 2575 | "eyeColor": "green", 2576 | "name": "Mallory Frazier", 2577 | "gender": "female", 2578 | "company": "BITTOR", 2579 | "email": "malloryfrazier@bittor.com", 2580 | "phone": "+1 (987) 491-3058", 2581 | "address": "346 Court Square, Gordon, Minnesota, 5295", 2582 | "about": "Ex tempor amet aute est consectetur laboris enim anim labore ea ullamco nulla. In et consequat proident et sunt sunt eu commodo esse non ea elit. Fugiat ipsum do excepteur incididunt officia tempor in ea dolor enim.\r\n", 2583 | "registered": "2018-05-01T03:25:00 -01:00", 2584 | "latitude": 55.006732, 2585 | "longitude": -0.056633, 2586 | "tags": [ 2587 | "cupidatat", 2588 | "amet", 2589 | "deserunt", 2590 | "cupidatat", 2591 | "excepteur", 2592 | "culpa", 2593 | "quis" 2594 | ], 2595 | "friends": [ 2596 | { 2597 | "id": 0, 2598 | "name": "Cora Mayo" 2599 | }, 2600 | { 2601 | "id": 1, 2602 | "name": "Abby Hayden" 2603 | }, 2604 | { 2605 | "id": 2, 2606 | "name": "Colleen Abbott" 2607 | } 2608 | ], 2609 | "greeting": "Hello, Mallory Frazier! You have 2 unread messages.", 2610 | "favoriteFruit": "banana" 2611 | }, 2612 | { 2613 | "_id": "5dddbc7cc3611bc4b31eded5", 2614 | "index": 58, 2615 | "guid": "120d128e-515f-4327-bba3-309a9cc4be62", 2616 | "isActive": true, 2617 | "balance": "$2,640.72", 2618 | "picture": "http://placehold.it/32x32", 2619 | "age": 39, 2620 | "eyeColor": "blue", 2621 | "name": "Terrell Mcneil", 2622 | "gender": "male", 2623 | "company": "GEOFARM", 2624 | "email": "terrellmcneil@geofarm.com", 2625 | "phone": "+1 (826) 487-2849", 2626 | "address": "303 Livonia Avenue, Deltaville, West Virginia, 667", 2627 | "about": "Eiusmod deserunt aliquip irure tempor cupidatat minim. Ex est sit reprehenderit incididunt. Irure non ex officia enim aliqua culpa. Laboris dolore cillum amet nisi. Ex amet commodo proident duis adipisicing veniam pariatur ut dolore aute ullamco irure consequat. Nostrud aute velit nisi ex commodo in culpa ipsum voluptate.\r\n", 2628 | "registered": "2018-11-28T12:33:25 -00:00", 2629 | "latitude": -66.648912, 2630 | "longitude": 83.763483, 2631 | "tags": [ 2632 | "veniam", 2633 | "laborum", 2634 | "quis", 2635 | "ipsum", 2636 | "aliquip", 2637 | "consequat", 2638 | "cupidatat" 2639 | ], 2640 | "friends": [ 2641 | { 2642 | "id": 0, 2643 | "name": "Compton Tucker" 2644 | }, 2645 | { 2646 | "id": 1, 2647 | "name": "Odessa Hubbard" 2648 | }, 2649 | { 2650 | "id": 2, 2651 | "name": "Marcella Levy" 2652 | } 2653 | ], 2654 | "greeting": "Hello, Terrell Mcneil! You have 2 unread messages.", 2655 | "favoriteFruit": "banana" 2656 | }, 2657 | { 2658 | "_id": "5dddbc7cd1ccb6b92965111d", 2659 | "index": 59, 2660 | "guid": "36ed43ba-0bd5-44aa-a581-60e4ed034e44", 2661 | "isActive": true, 2662 | "balance": "$2,101.59", 2663 | "picture": "http://placehold.it/32x32", 2664 | "age": 31, 2665 | "eyeColor": "brown", 2666 | "name": "Ernestine Warren", 2667 | "gender": "female", 2668 | "company": "TERRAGEN", 2669 | "email": "ernestinewarren@terragen.com", 2670 | "phone": "+1 (902) 509-3916", 2671 | "address": "165 Main Street, Camas, Washington, 5886", 2672 | "about": "Voluptate laborum nulla incididunt dolor do Lorem aute aliqua. Laborum tempor nostrud mollit aute excepteur. Nulla velit labore magna irure est consectetur culpa nulla dolore id incididunt esse aute. Tempor est fugiat deserunt occaecat nisi magna nulla est adipisicing tempor cillum. Commodo incididunt Lorem nostrud aliquip exercitation cupidatat Lorem nostrud minim consectetur eiusmod in incididunt commodo.\r\n", 2673 | "registered": "2017-06-01T11:51:46 -01:00", 2674 | "latitude": -83.606016, 2675 | "longitude": 46.809298, 2676 | "tags": [ 2677 | "voluptate", 2678 | "mollit", 2679 | "deserunt", 2680 | "ex", 2681 | "et", 2682 | "sit", 2683 | "voluptate" 2684 | ], 2685 | "friends": [ 2686 | { 2687 | "id": 0, 2688 | "name": "Jessica Macdonald" 2689 | }, 2690 | { 2691 | "id": 1, 2692 | "name": "Virginia Black" 2693 | }, 2694 | { 2695 | "id": 2, 2696 | "name": "Talley Steele" 2697 | } 2698 | ], 2699 | "greeting": "Hello, Ernestine Warren! You have 10 unread messages.", 2700 | "favoriteFruit": "banana" 2701 | }, 2702 | { 2703 | "_id": "5dddbc7c90214d1e5c29ec1a", 2704 | "index": 60, 2705 | "guid": "56d4ac27-9bd6-438a-9757-909f65eae7ad", 2706 | "isActive": false, 2707 | "balance": "$2,293.98", 2708 | "picture": "http://placehold.it/32x32", 2709 | "age": 32, 2710 | "eyeColor": "green", 2711 | "name": "Tisha Mcintosh", 2712 | "gender": "female", 2713 | "company": "GOLOGY", 2714 | "email": "tishamcintosh@gology.com", 2715 | "phone": "+1 (823) 597-2108", 2716 | "address": "222 Henderson Walk, Ogema, Marshall Islands, 557", 2717 | "about": "Ea duis ipsum officia adipisicing qui elit elit pariatur et occaecat sint do. Minim cupidatat deserunt duis non incididunt dolor exercitation. Cupidatat non occaecat laboris eu sint sunt proident labore. Ex exercitation officia minim quis.\r\n", 2718 | "registered": "2017-04-29T10:40:47 -01:00", 2719 | "latitude": -32.138953, 2720 | "longitude": 46.371255, 2721 | "tags": [ 2722 | "ipsum", 2723 | "deserunt", 2724 | "do", 2725 | "voluptate", 2726 | "non", 2727 | "ad", 2728 | "pariatur" 2729 | ], 2730 | "friends": [ 2731 | { 2732 | "id": 0, 2733 | "name": "Geneva Fletcher" 2734 | }, 2735 | { 2736 | "id": 1, 2737 | "name": "Edna Gardner" 2738 | }, 2739 | { 2740 | "id": 2, 2741 | "name": "Ball Dennis" 2742 | } 2743 | ], 2744 | "greeting": "Hello, Tisha Mcintosh! You have 8 unread messages.", 2745 | "favoriteFruit": "apple" 2746 | }, 2747 | { 2748 | "_id": "5dddbc7cad8754ea1939fa77", 2749 | "index": 61, 2750 | "guid": "b63bc856-ffa5-42a5-9a01-fca974309fc8", 2751 | "isActive": false, 2752 | "balance": "$1,323.68", 2753 | "picture": "http://placehold.it/32x32", 2754 | "age": 37, 2755 | "eyeColor": "brown", 2756 | "name": "Katheryn Ochoa", 2757 | "gender": "female", 2758 | "company": "TERRASYS", 2759 | "email": "katherynochoa@terrasys.com", 2760 | "phone": "+1 (879) 514-3267", 2761 | "address": "156 Apollo Street, Bellamy, Nebraska, 1850", 2762 | "about": "Minim sit sit quis culpa enim sit cupidatat ipsum enim. Pariatur magna commodo minim dolore voluptate duis dolore. Laboris excepteur dolore pariatur consequat consectetur commodo. Anim ullamco tempor incididunt pariatur culpa pariatur id velit quis quis incididunt sint enim enim. Nisi fugiat aliquip esse elit.\r\n", 2763 | "registered": "2016-01-10T05:38:12 -00:00", 2764 | "latitude": 1.700449, 2765 | "longitude": -73.219078, 2766 | "tags": [ 2767 | "Lorem", 2768 | "eu", 2769 | "incididunt", 2770 | "sit", 2771 | "cillum", 2772 | "sint", 2773 | "laboris" 2774 | ], 2775 | "friends": [ 2776 | { 2777 | "id": 0, 2778 | "name": "Benita Griffith" 2779 | }, 2780 | { 2781 | "id": 1, 2782 | "name": "Taylor Burgess" 2783 | }, 2784 | { 2785 | "id": 2, 2786 | "name": "Theresa Nelson" 2787 | } 2788 | ], 2789 | "greeting": "Hello, Katheryn Ochoa! You have 3 unread messages.", 2790 | "favoriteFruit": "strawberry" 2791 | }, 2792 | { 2793 | "_id": "5dddbc7cdb6dbcca1df0af02", 2794 | "index": 62, 2795 | "guid": "2fa4a23c-332c-47d6-9be3-3e7641d4f522", 2796 | "isActive": true, 2797 | "balance": "$1,503.96", 2798 | "picture": "http://placehold.it/32x32", 2799 | "age": 40, 2800 | "eyeColor": "blue", 2801 | "name": "Martha Stephenson", 2802 | "gender": "female", 2803 | "company": "PAPRIKUT", 2804 | "email": "marthastephenson@paprikut.com", 2805 | "phone": "+1 (810) 557-3356", 2806 | "address": "367 Gunnison Court, Goodville, Florida, 3319", 2807 | "about": "Aute ad enim ullamco laboris dolor dolor deserunt cillum duis. Sit velit laboris sunt nulla cupidatat do labore elit ullamco. Proident velit consequat occaecat sint ut tempor esse esse ex aliquip commodo est do. Nostrud cillum exercitation adipisicing magna aute aliqua proident non mollit quis et ullamco in ipsum. Consequat nostrud velit magna ut esse officia. Aute fugiat do ad deserunt magna pariatur incididunt.\r\n", 2808 | "registered": "2019-04-01T01:18:51 -01:00", 2809 | "latitude": -54.580521, 2810 | "longitude": -172.591279, 2811 | "tags": [ 2812 | "eu", 2813 | "minim", 2814 | "ullamco", 2815 | "est", 2816 | "et", 2817 | "labore", 2818 | "elit" 2819 | ], 2820 | "friends": [ 2821 | { 2822 | "id": 0, 2823 | "name": "Bobbie Gilbert" 2824 | }, 2825 | { 2826 | "id": 1, 2827 | "name": "Dana Molina" 2828 | }, 2829 | { 2830 | "id": 2, 2831 | "name": "Shelby Boyle" 2832 | } 2833 | ], 2834 | "greeting": "Hello, Martha Stephenson! You have 7 unread messages.", 2835 | "favoriteFruit": "banana" 2836 | }, 2837 | { 2838 | "_id": "5dddbc7c0e6f110998bd1cd1", 2839 | "index": 63, 2840 | "guid": "4cce012a-29ec-4b45-9a48-002708c64b59", 2841 | "isActive": false, 2842 | "balance": "$2,485.02", 2843 | "picture": "http://placehold.it/32x32", 2844 | "age": 32, 2845 | "eyeColor": "brown", 2846 | "name": "Reyna William", 2847 | "gender": "female", 2848 | "company": "TALKOLA", 2849 | "email": "reynawilliam@talkola.com", 2850 | "phone": "+1 (922) 440-3593", 2851 | "address": "540 Rose Street, Chautauqua, Hawaii, 3139", 2852 | "about": "Id adipisicing aliqua voluptate voluptate minim quis id eiusmod et. Magna nisi officia exercitation do aliquip minim ea ullamco enim. Nulla sunt voluptate qui ea aliquip in laborum commodo id aliqua do anim nostrud commodo. Quis aliqua irure reprehenderit sit commodo mollit irure est elit eiusmod ipsum aliquip. Nisi officia esse officia officia.\r\n", 2853 | "registered": "2016-09-14T12:04:04 -01:00", 2854 | "latitude": 58.462788, 2855 | "longitude": -156.61224, 2856 | "tags": [ 2857 | "laborum", 2858 | "sit", 2859 | "deserunt", 2860 | "nostrud", 2861 | "magna", 2862 | "fugiat", 2863 | "amet" 2864 | ], 2865 | "friends": [ 2866 | { 2867 | "id": 0, 2868 | "name": "Sherry Nichols" 2869 | }, 2870 | { 2871 | "id": 1, 2872 | "name": "Silvia Lane" 2873 | }, 2874 | { 2875 | "id": 2, 2876 | "name": "Susan Cardenas" 2877 | } 2878 | ], 2879 | "greeting": "Hello, Reyna William! You have 5 unread messages.", 2880 | "favoriteFruit": "strawberry" 2881 | }, 2882 | { 2883 | "_id": "5dddbc7c41e103728883e0de", 2884 | "index": 64, 2885 | "guid": "b5cf97dc-c77e-4f30-8669-fbebfda1c154", 2886 | "isActive": false, 2887 | "balance": "$3,830.62", 2888 | "picture": "http://placehold.it/32x32", 2889 | "age": 21, 2890 | "eyeColor": "blue", 2891 | "name": "Paul Medina", 2892 | "gender": "male", 2893 | "company": "ENDIPINE", 2894 | "email": "paulmedina@endipine.com", 2895 | "phone": "+1 (845) 468-3614", 2896 | "address": "537 Pooles Lane, Sanders, New Jersey, 2979", 2897 | "about": "Ut culpa et tempor quis ex in velit magna cupidatat aliqua eu nostrud proident sit. Ea cupidatat cillum veniam elit. Duis enim deserunt deserunt laborum proident consequat excepteur sunt sunt proident amet.\r\n", 2898 | "registered": "2016-12-30T02:35:20 -00:00", 2899 | "latitude": 62.39953, 2900 | "longitude": 163.574703, 2901 | "tags": [ 2902 | "voluptate", 2903 | "deserunt", 2904 | "tempor", 2905 | "laborum", 2906 | "cillum", 2907 | "eu", 2908 | "anim" 2909 | ], 2910 | "friends": [ 2911 | { 2912 | "id": 0, 2913 | "name": "Krystal Christian" 2914 | }, 2915 | { 2916 | "id": 1, 2917 | "name": "Lilly Dudley" 2918 | }, 2919 | { 2920 | "id": 2, 2921 | "name": "Massey Pickett" 2922 | } 2923 | ], 2924 | "greeting": "Hello, Paul Medina! You have 8 unread messages.", 2925 | "favoriteFruit": "banana" 2926 | }, 2927 | { 2928 | "_id": "5dddbc7c3aec256cf35c5a07", 2929 | "index": 65, 2930 | "guid": "e106f3ea-1abb-45e9-9d18-f2d6246c3286", 2931 | "isActive": true, 2932 | "balance": "$2,795.01", 2933 | "picture": "http://placehold.it/32x32", 2934 | "age": 24, 2935 | "eyeColor": "blue", 2936 | "name": "Blevins Fleming", 2937 | "gender": "male", 2938 | "company": "BULLJUICE", 2939 | "email": "blevinsfleming@bulljuice.com", 2940 | "phone": "+1 (828) 561-2489", 2941 | "address": "524 Danforth Street, Joppa, Tennessee, 9863", 2942 | "about": "Anim consectetur ullamco cupidatat duis dolore elit cupidatat id pariatur ut. Ad adipisicing sit laboris non est occaecat ut aliquip amet pariatur incididunt. Nulla anim dolore sunt consectetur ipsum do ullamco. Lorem qui cupidatat laborum aute occaecat esse Lorem enim. Veniam quis mollit sint velit laboris ad. Mollit cupidatat qui consequat nisi.\r\n", 2943 | "registered": "2015-03-18T04:15:11 -00:00", 2944 | "latitude": 48.028891, 2945 | "longitude": 162.060098, 2946 | "tags": [ 2947 | "nostrud", 2948 | "deserunt", 2949 | "non", 2950 | "consequat", 2951 | "esse", 2952 | "eu", 2953 | "minim" 2954 | ], 2955 | "friends": [ 2956 | { 2957 | "id": 0, 2958 | "name": "Carlene Ashley" 2959 | }, 2960 | { 2961 | "id": 1, 2962 | "name": "Cathy Waters" 2963 | }, 2964 | { 2965 | "id": 2, 2966 | "name": "Cannon Wagner" 2967 | } 2968 | ], 2969 | "greeting": "Hello, Blevins Fleming! You have 8 unread messages.", 2970 | "favoriteFruit": "apple" 2971 | }, 2972 | { 2973 | "_id": "5dddbc7c6cdc1b80d1db836d", 2974 | "index": 66, 2975 | "guid": "16907e2d-8cb0-4fda-a124-dc48efd3eee5", 2976 | "isActive": false, 2977 | "balance": "$1,438.57", 2978 | "picture": "http://placehold.it/32x32", 2979 | "age": 30, 2980 | "eyeColor": "green", 2981 | "name": "Socorro Greer", 2982 | "gender": "female", 2983 | "company": "ACRUEX", 2984 | "email": "socorrogreer@acruex.com", 2985 | "phone": "+1 (862) 467-3433", 2986 | "address": "151 Bulwer Place, Snyderville, Puerto Rico, 8892", 2987 | "about": "Proident Lorem non culpa laborum dolore est consectetur labore dolor laboris. Aliqua dolore voluptate mollit nostrud veniam ipsum consectetur ad sunt aliqua id culpa. Sint aute amet veniam ad ipsum quis anim ea ipsum. Aliquip aliqua proident aliquip sunt sunt in pariatur nulla incididunt occaecat. Duis est ullamco qui sunt. Laborum tempor veniam nisi est laboris ex nisi.\r\n", 2988 | "registered": "2015-12-27T03:33:38 -00:00", 2989 | "latitude": -46.051795, 2990 | "longitude": -129.284085, 2991 | "tags": [ 2992 | "nostrud", 2993 | "amet", 2994 | "ullamco", 2995 | "consequat", 2996 | "reprehenderit", 2997 | "id", 2998 | "ullamco" 2999 | ], 3000 | "friends": [ 3001 | { 3002 | "id": 0, 3003 | "name": "Lacey Hansen" 3004 | }, 3005 | { 3006 | "id": 1, 3007 | "name": "Sheree Estrada" 3008 | }, 3009 | { 3010 | "id": 2, 3011 | "name": "Mcbride Stone" 3012 | } 3013 | ], 3014 | "greeting": "Hello, Socorro Greer! You have 10 unread messages.", 3015 | "favoriteFruit": "banana" 3016 | } 3017 | ] 3018 | --------------------------------------------------------------------------------