Created
May 9, 2014 04:53
-
-
Save Kintaro/bab6bdc9b3b5129b1ba3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub fn main() { | |
let f = action(floats(), p); | |
let parser = spaces() << string_s("vertex") << string_s("[") << | |
f << spaces() << f << spaces() << f << spaces() << string("]"); | |
let input = ParserInput::new(std::os::args()[1]); | |
let result = parser.parse(&input); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![crate_id="parser#0.0.1"] | |
#![comment = "Simple Parser Library"] | |
#![license = "BSD"] | |
#![crate_type = "lib"] | |
use std::owned::Box; | |
/// Holds the result of a parser. | |
/// The tuple holds the parsed input and the remaining input. | |
/// None if the parser failed | |
pub type ParseResult = Option<(~str, ~str)>; | |
pub type ParserT = Box<Parser>; | |
/// Holds the parser input | |
pub struct ParserInput { | |
input: ~str | |
} | |
impl ParserInput { | |
/// Creates a new parser input from the given string | |
pub fn new(x: &str) -> ParserInput { | |
ParserInput { input: x.to_owned() } | |
} | |
/// Returns the first character in the parser input | |
pub fn first(&self) -> char { | |
self.input.chars().nth(0).unwrap() | |
} | |
/// Returns everything after the first character in the parser input | |
pub fn rest(&self) -> ~str { | |
self.input.chars().skip(1).collect() | |
} | |
/// Returns true if no input is remaining | |
pub fn at_end(&self) -> bool { | |
self.input.len() == 0 | |
} | |
/// Returns the complete input, including the first character | |
pub fn input(&self) -> ~str { | |
self.input.clone() | |
} | |
} | |
/// A parser | |
pub trait Parser { | |
fn parse(&self, input: &ParserInput) -> ParseResult; | |
fn to_owned(&self) -> ParserT; | |
} | |
/// A parser that recognizes single digits and fails otherwise | |
pub struct DigitParser; | |
impl DigitParser { | |
pub fn new() -> ParserT { box DigitParser as ParserT } | |
} | |
impl Parser for DigitParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
if input.first().is_digit() { | |
Some((std::str::from_char(input.first()), input.rest())) | |
} else { | |
None | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
DigitParser::new() | |
} | |
} | |
/// A parser that recognizes single alphabetic characters | |
pub struct AlphaParser; | |
impl AlphaParser { | |
pub fn new() -> ParserT { | |
box AlphaParser as ParserT | |
} | |
} | |
impl Parser for AlphaParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
if input.first().is_alphabetic() { | |
Some((std::str::from_char(input.first()), input.rest())) | |
} else { | |
None | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
AlphaParser::new() | |
} | |
} | |
/// A parser that first tries to match the first parser | |
/// and if that fails tries the second parser | |
pub struct ChoiceParser { | |
a: ParserT, | |
b: ParserT | |
} | |
impl ChoiceParser { | |
pub fn new(a: ParserT, b: ParserT) -> ParserT { | |
box ChoiceParser { a: a, b: b } as ParserT | |
} | |
} | |
impl Parser for ChoiceParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
self.a.parse(input).or(self.b.parse(input)) | |
} | |
fn to_owned(&self) -> ParserT { | |
ChoiceParser::new(self.a.to_owned(), self.b.to_owned()) | |
} | |
} | |
impl BitOr<ParserT, ParserT> for ParserT { | |
fn bitor(&self, rhs: &ParserT) -> ParserT { | |
ChoiceParser::new(self.to_owned(), rhs.to_owned()) | |
} | |
} | |
/// A parser that matches both parsers in a row. | |
/// If one of them fails, the SequenceParser also fails. | |
pub struct SequenceParser { | |
a: ParserT, | |
b: ParserT | |
} | |
impl SequenceParser { | |
pub fn new(a: ParserT, b: ParserT) -> ParserT { | |
box SequenceParser { a: a, b: b } as ParserT | |
} | |
} | |
impl Parser for SequenceParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
self.a.parse(input).and_then(|(m, r)| { | |
match self.b.parse(&ParserInput::new(r)) { | |
Some ((p, s)) => Some ((m.append(p), s)), | |
_ => None | |
} | |
}) | |
} | |
fn to_owned(&self) -> ParserT { | |
SequenceParser::new(self.a.to_owned(), self.b.to_owned()) | |
} | |
} | |
impl Shl<ParserT, ParserT> for ParserT { | |
fn shl(&self, rhs: &ParserT) -> ParserT { | |
SequenceParser::new(self.to_owned(), rhs.to_owned()) | |
} | |
} | |
pub struct ShortestParser { | |
a: ParserT, | |
b: ParserT | |
} | |
impl ShortestParser { | |
pub fn new(a: ParserT, b: ParserT) -> ParserT { | |
box ShortestParser { a: a, b: b } as ParserT | |
} | |
} | |
impl Parser for ShortestParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
let x = self.a.parse(input); | |
let y = self.b.parse(input); | |
match (x, y) { | |
(Some((a, r1)), Some((b, r2))) => { | |
if a.len() <= b.len() { | |
Some((a, r1)) | |
} else { | |
Some((b, r2)) | |
} | |
}, | |
(None, None) => None, | |
( a, None) => a, | |
(None, b) => b | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
ShortestParser::new(self.a.to_owned(), self.b.to_owned()) | |
} | |
} | |
pub struct LongestParser { | |
a: ParserT, | |
b: ParserT | |
} | |
impl LongestParser { | |
pub fn new(a: ParserT, b: ParserT) -> ParserT { | |
box LongestParser { a: a, b: b } as ParserT | |
} | |
} | |
impl Parser for LongestParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
let x = self.a.parse(input); | |
let y = self.b.parse(input); | |
match (x, y) { | |
(Some((a, r1)), Some((b, r2))) => { | |
if a.len() >= b.len() { | |
Some((a, r1)) | |
} else { | |
Some((b, r2)) | |
} | |
}, | |
(None, None) => None, | |
( a, None) => a, | |
(None, b) => b | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
LongestParser::new(self.a.to_owned(), self.b.to_owned()) | |
} | |
} | |
pub struct SkipParser { | |
parser: ParserT | |
} | |
impl SkipParser { | |
pub fn new(a: ParserT) -> ParserT { | |
box SkipParser { parser: a } as ParserT | |
} | |
} | |
impl Parser for SkipParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
match self.parser.parse(input) { | |
Some((_, r)) => Some(("".to_owned(), r)), | |
None => None | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
SkipParser::new(self.parser.to_owned()) | |
} | |
} | |
pub struct FailParser { | |
message: ~str | |
} | |
impl FailParser { | |
pub fn new(msg: ~str) -> ParserT { | |
box FailParser { message: msg } as ParserT | |
} | |
} | |
impl Parser for FailParser { | |
fn parse(&self, _: &ParserInput) -> ParseResult { | |
None | |
} | |
fn to_owned(&self) -> ParserT { | |
FailParser::new(self.message.clone()) | |
} | |
} | |
pub struct OptionParser { | |
parser: ParserT | |
} | |
impl OptionParser { | |
pub fn new(a: ParserT) -> ParserT { | |
box OptionParser { parser: a } as ParserT | |
} | |
} | |
impl Parser for OptionParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
if input.at_end() { | |
return Some(("".to_owned(), input.input())); | |
} | |
match self.parser.parse(input) { | |
Some(x) => Some(x), | |
_ => Some(("".to_owned(), input.input())) | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
OptionParser::new(self.parser.to_owned()) | |
} | |
} | |
impl Neg<ParserT> for ParserT { | |
fn neg(&self) -> ParserT { | |
OptionParser::new(self.to_owned()) | |
} | |
} | |
/// A parser that matches on at least one or more | |
/// instances of the given parser | |
pub struct ManyParser { | |
parser: ParserT | |
} | |
impl ManyParser { | |
pub fn new(a: ParserT) -> ParserT { | |
box ManyParser { parser: a } as ParserT | |
} | |
} | |
impl Parser for ManyParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
match self.parser.parse(input) { | |
Some((ref a, ref b)) => { | |
let mut result = (a.clone(), b.clone()); | |
loop { | |
let (i, r) = result.clone(); | |
if r.len() == 0 { | |
break; | |
} | |
match self.parser.parse(&ParserInput::new(r)) { | |
Some((x, r)) => { | |
result = (i.append(x), r); | |
}, | |
_ => break | |
} | |
} | |
Some(result) | |
}, | |
_ => None | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
ManyParser::new(self.parser.to_owned()) | |
} | |
} | |
impl Not<ParserT> for ParserT { | |
fn not(&self) -> ParserT { | |
ManyParser::new(self.to_owned()) | |
} | |
} | |
/// A parser that only matches on the given character | |
pub struct CharParser { | |
c: char | |
} | |
impl CharParser { | |
pub fn new(c: char) -> ParserT { | |
box CharParser { c: c } as ParserT | |
} | |
} | |
impl Parser for CharParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
match input.first() { | |
x if x == self.c => Some((std::str::from_char(x), input.rest())), | |
_ => None | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
CharParser::new(self.c) | |
} | |
} | |
/// A parser that always matches on zero input | |
pub struct EmptyParser; | |
impl EmptyParser { | |
pub fn new() -> ParserT { | |
box EmptyParser as ParserT | |
} | |
} | |
impl Parser for EmptyParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
Some(("".to_owned(), input.input())) | |
} | |
fn to_owned(&self) -> ParserT { | |
EmptyParser::new() | |
} | |
} | |
/// A parser that matches on any character in the given set | |
pub struct SetParser { | |
chars: ~str | |
} | |
impl SetParser { | |
/// Creates a new SetParser | |
pub fn new(x: ~str) -> ParserT { | |
box SetParser { chars: x } as ParserT | |
} | |
} | |
impl Parser for SetParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
self.chars.chars().filter_map(|x| { CharParser::new(x).parse(input) }).nth(0) | |
} | |
fn to_owned(&self) -> ParserT { | |
SetParser::new(self.chars.clone()) | |
} | |
} | |
pub struct ActionParser { | |
parser: ParserT, | |
function: fn(~str) -> () | |
} | |
impl ActionParser { | |
pub fn new(parser: ParserT, f: fn(~str) -> ()) -> ParserT { | |
box ActionParser { parser: parser.to_owned(), function: f } as ParserT | |
} | |
} | |
impl Parser for ActionParser { | |
fn parse(&self, input: &ParserInput) -> ParseResult { | |
match self.parser.parse(input) { | |
Some((m, r)) => { | |
(self.function)(m.clone()); | |
Some((m, r)) | |
} | |
_ => None | |
} | |
} | |
fn to_owned(&self) -> ParserT { | |
ActionParser::new(self.parser.to_owned(), self.function) | |
} | |
} | |
/// Creates a ManyParser from the given parser | |
pub fn many(p: ParserT) -> ParserT { | |
ManyParser::new(p) | |
} | |
/// Creates an OptionParser from the given parser | |
pub fn option(p: ParserT) -> ParserT { | |
OptionParser::new(p) | |
} | |
/// Creates a DigitParser | |
pub fn digit() -> ParserT { | |
DigitParser::new() | |
} | |
/// Creates a ManyParser(DigitParser) that recognizes | |
/// a sequence of digits | |
pub fn digits() -> ParserT { | |
!(digit()) | |
} | |
/// Creates an AlphaParser that recognizes single alphabetic | |
/// characters | |
pub fn alphabetic() -> ParserT { | |
AlphaParser::new() | |
} | |
/// Creates a parser that matches on floats | |
pub fn floats() -> ParserT { | |
// Optional leading - sign | |
(-CharParser::new('-')) << | |
// Digits + optional fractional part | |
( (digits() << -(CharParser::new('.') << -(digits()))) | | |
// or optional digits + fractional part | |
(-(digits()) << (CharParser::new('.') << digits()))) << | |
// + optional exponent | |
-(CharParser::new('e') << (-CharParser::new('-')) << digits()) | |
} | |
/// Creates a parser that recognizes alpha-numeric characters | |
pub fn alphnum() -> ParserT { | |
digit() | alphabetic() | |
} | |
/// Creates a parser that recognizes one or more alpha-numeric characters | |
pub fn alphnums() -> ParserT { | |
!(alphnum()) | |
} | |
/// Creates a set parser | |
pub fn set(x: ~str) -> ParserT { | |
SetParser::new(x) | |
} | |
/// Creates a new action parser | |
pub fn action(parser: ParserT, f: fn(~str) -> ()) -> ParserT { | |
ActionParser::new(parser, f) | |
} | |
/// Creates a parser that matches on spaces and tabs | |
pub fn space() -> ParserT { | |
set(" \t\r\n".to_owned()) | |
} | |
/// Creates a parser that matches on one or more spaces and/or tabs | |
pub fn spaces() -> ParserT { | |
!(space()) | |
} | |
/// Creates an empty parser that always matches on zero input | |
pub fn empty() -> ParserT { | |
EmptyParser::new() | |
} | |
/// Creates a parser that matches the given string | |
pub fn string(x: &str) -> ParserT { | |
x.chars().fold(empty(), |a, i| { a << CharParser::new(i) }) | |
} | |
/// Creates a parser that matches | |
pub fn string_s(x: &str) -> ParserT { | |
-(spaces()) << string(x) << -(spaces()) | |
} | |
pub fn skip(x: ParserT) -> ParserT { | |
SkipParser::new(x) | |
} | |
pub fn skip_many(x: ParserT) -> ParserT { | |
!(skip(x)) | |
} | |
pub fn p(x: ~str) { | |
let f : f32 = std::from_str::from_str(x).unwrap(); | |
println!("Found vector element {}", f); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment