Skip to content

Instantly share code, notes, and snippets.

@Kintaro
Created May 9, 2014 04:53
Show Gist options
  • Save Kintaro/bab6bdc9b3b5129b1ba3 to your computer and use it in GitHub Desktop.
Save Kintaro/bab6bdc9b3b5129b1ba3 to your computer and use it in GitHub Desktop.
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);
}
#![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