peg-syntax-ext 0.5.7

Deprecated syntax extension plugin for Rust nightly. See the `peg` crate for other integration options.
#![feature(plugin)]
#![plugin(peg_syntax_ext)]

use std::collections::HashMap;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Identifier(usize);

pub struct Interner {
    forward: HashMap<String, Identifier>,
    backward: Vec<String>
}

impl Interner {
    fn new() -> Interner {
        Interner { forward: HashMap::new(), backward: Vec::new() }
    }

    fn intern(&mut self, s: &str) -> Identifier {
        if let Some(&id) = self.forward.get(s) { id } else {
            let id = Identifier(self.backward.len());
            self.backward.push(s.to_owned());
            self.forward.insert(s.to_owned(), id);
            id
        }
    }
}

#[derive(Eq, PartialEq, Debug)]
pub struct Spanned<T> {
    file_id: usize,
    start: usize,
    end: usize,
    node: T,
}

impl<T> Spanned<T> {
    fn with_node<U>(&self, node: U) -> Spanned<U> {
        Spanned { file_id: self.file_id, start: self.start, end: self.end, node }
    }
}

#[derive(Eq, PartialEq, Debug)]
pub enum Node {
    Variable(Identifier),
    Add(Box<Spanned<Node>>, Box<Spanned<Node>>),
    Mul(Box<Spanned<Node>>, Box<Spanned<Node>>),
}

peg! parser(r#"
use super::{Interner, Identifier, Spanned, Node};

#![arguments(file_id: usize, interner: &mut Interner)]

pub outer_rule -> Vec<Spanned<Identifier>> = inner_rule**","

inner_rule -> Spanned<Identifier> = spanned<identifier>

spanned<X>
    = start:#position node:X end:#position
    { Spanned { file_id, start, end, node} }

identifier -> Identifier
    = s:$([a-z]+) { interner.intern(s) }

variable -> Spanned<Node>
    = v:spanned<identifier>
    { v.with_node(Node::Variable(v.node)) }

pub arith_ast -> Spanned<Node> = #infix<variable> {
	#L x op:spanned<"+"> y { op.with_node(Node::Add(Box::new(x), Box::new(y))) }
	#L x op:spanned<"*"> y { op.with_node(Node::Mul(Box::new(x), Box::new(y))) }
}

"#);

#[test]
fn test_grammar_args() {
    let mut interner = Interner::new();
    let result = parser::outer_rule("abcd,xyz,foobar,xyz", 23, &mut interner).unwrap();

    assert_eq!(result, vec![
        Spanned { file_id: 23, start: 0,  end: 4,  node: Identifier(0) },
        Spanned { file_id: 23, start: 5,  end: 8,  node: Identifier(1) },
        Spanned { file_id: 23, start: 9,  end: 15, node: Identifier(2) },
        Spanned { file_id: 23, start: 16, end: 19, node: Identifier(1) },
    ]);
}

#[test]
fn test_grammar_args_infix() {
    let mut interner = Interner::new();
    let result = parser::arith_ast("baz*foo+baz", 55, &mut interner).unwrap();
    assert_eq!(result, Spanned { file_id: 55, start: 7, end: 8, node:
        Node::Add(
            Box::new(Spanned { file_id: 55, start: 3, end: 4, node:
                Node::Mul(
                    Box::new(Spanned { file_id: 55, start: 0, end: 3, node: Node::Variable(Identifier(0))}),
                    Box::new(Spanned { file_id: 55, start: 4, end: 7, node: Node::Variable(Identifier(1))})
                )
            }),
            Box::new(Spanned { file_id: 55, start: 8, end: 11, node: Node::Variable(Identifier(0))})
        )
    })
}