p.enthalabs

grammar-to-marser — grammar → Marser Rust converter

use marser::capture;

use marser::matcher::{

AnyToken,

Matcher,

many,

negative_lookahead,

one_or_more,

optional,

start_of_input,

end_of_input,

};

use marser::one_of::one_of;

use marser::parser::{

Parser,

};

// Inserts whitespace between repetitions, but not before the first item.

// This keeps `X*` equivalent to the source grammar while avoiding duplicated

// generated matcher bodies.

fn repeat_ws<'src,MRes,Item,Ws>(

item: Item,

ws: Ws,

)->impl Matcher<'src,&'src str,MRes> + Clone

where

Item: Matcher<'src,&'src str,MRes> + Clone,

Ws: Matcher<'src,&'src str,MRes> + Clone,

{

optional((item.clone(),many((ws,item))))

}

// Typed parse tree returned by `grammar()`. Each grammar rule becomes a variant;

// labeled bindings become struct fields, and leaf rules store their matched slice

// as `value`.

#[derive(Debug,Clone,PartialEq)]

pub enum Parsed<'src>{

main{

item_val: Vec<Box<Parsed<'src>>>,

},

item{

name: Box<Parsed<'src>>,

value: Box<Parsed<'src>>,

},

ident{value: &'src str},

number{value: &'src str},

}

// Returns a complete parser for this grammar.

// Usage: grammar().parse_str(src) → Ok((Parsed, errors))

pub fn grammar<'src>()->impl Parser<'src,&'src str,Output=Parsed<'src>> + Clone{

let ASCII_ALPHA=one_of(('a'..='z','A'..='Z'));

let ASCII_ALPHANUMERIC=one_of(('a'..='z','A'..='Z','0'..='9'));

let ASCII_DIGIT='0'..='9';

// number = @{ ASCII_DIGIT+ }

let number=capture!(

bind_slice!(one_or_more(ASCII_DIGIT.clone()),value as&'src str)=>Parsed::number{value}

);

// ident = @{ ("_" | ASCII_ALPHA) ~ ("_" | ASCII_ALPHANUMERIC)* }

let ident=capture!(

bind_slice!(

(

one_of(('_',ASCII_ALPHA.clone())),

many(one_of(('_',ASCII_ALPHANUMERIC.clone()))),

),

value as&'src str

)=>Parsed::ident{value}

);

// newline = _{ "\n" | "\r\n" }

let newline=one_of(('\n',"\r\n"));

// WHITESPACE = _{ " " | "\t" | newline }

let WHITESPACE=one_of((' ','\t',newline.clone()));

// line_comment = _{ "//" ~ (!newline ~ ANY)* }

let line_comment=("//",many((negative_lookahead(newline.clone()),AnyToken)));

// COMMENT = _{ line_comment }

let COMMENT=line_comment.clone();

// Pest injects WHITESPACE (and COMMENT) between every `~` in non-atomic rules.

// ws.clone() appears between sequence elements throughout this file for that reason.

let ws=many(

one_of((WHITESPACE.clone(),COMMENT.clone()))

);

// item = { #name = ident ~ "=" ~ #value = number }

let item=capture!(

(

bind!(ident.clone(),name),

ws.clone(),

'=',

ws.clone(),

bind!(number.clone(),value),

)=>Parsed::item{

name: Box::new(name),

value: Box::new(value),

}

);

// main = { SOI ~ item ~ ("," ~ item)* ~ EOI }

let main=capture!(

(

start_of_input(),

ws.clone(),

bind!(item.clone(),*item_val),

ws.clone(),

repeat_ws(

(',',ws.clone(),bind!(item.clone(),*item_val)),

ws.clone(),

),

ws.clone(),

end_of_input(),

)=>Parsed::main{

item_val: item_val.into_iter().map(Box::new).collect(),

}

);

main.clone()

}