gll 0.0.2

GLL parsing framework.
Documentation
extern crate indexing;
extern crate ordermap;

// HACK(eddyb) bootstrap by including a subset of the `gll` crate.
#[path = "src/generate/mod.rs"]
mod generate;
#[path = "src/grammar.rs"]
mod grammar;
#[path = "src/scannerless.rs"]
mod scannerless;

use grammar::{call, eat, negative_lookahead};
use std::env;
use std::fs;
use std::path::PathBuf;

type Grammar = scannerless::Grammar<&'static str>;

// HACK(eddyb) more explicit subset of the grammar, for bootstrapping.
macro_rules! rule {
    ({ $start:tt ..= $end:tt }) => {
        eat($start..=$end)
    };
    ({ ! $pat:tt }) => {
        negative_lookahead($pat)
    };
    ({ ! $start:tt ..= $end:tt }) => {
        negative_lookahead($start..=$end)
    };
    ($rule:ident) => {
        call(stringify!($rule))
    };
    ({ $name:ident : $rule:tt }) => {
        rule!($rule).field(stringify!($name))
    };
    ({ $rule:tt ? }) => {
        rule!($rule).opt()
    };
    ({ $elem:tt * }) => {
        rule!($elem).repeat_many(None)
    };
    ({ $elem:tt + }) => {
        rule!($elem).repeat_more(None)
    };
    ({ $elem:tt + % $sep:tt }) => {
        rule!($elem).repeat_more(Some(rule!($sep)))
    };
    ({ $rule0:tt $(| $rule:tt)+ }) => {
        rule!($rule0) $(| rule!($rule))+
    };
    ({ $rule0:tt $($rule:tt)* }) => {
        rule!($rule0) $(+ rule!($rule))*
    };
    ($pat:expr) => {
        eat($pat)
    };
}

macro_rules! grammar {
    ($($rule_name:ident = $($rule:tt)|+;)*) => ({
        let mut grammar = Grammar::new();
        $(grammar.define(stringify!($rule_name), rule!({ $($rule)|+ }));)*
        grammar
    })
}

fn main() {
    println!("cargo:rerun-if-changed=build.rs");

    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

    let mut grammar = grammar!{
        // Lexical grammar.
        Whitespace = {
            {{
                " " | "\t" | "\n" | "\r" |
                { "//" {{ {!"\n"} .. }*} "\n" } |
                { "/*" {{ {!"*/"} .. }*} "*/" }
            }*}
            {!" "} {!"\t"} {!"\n"} {!"\r"} {!"//"} {!"/*"}
        };
        Shebang = { "#!" {{ {!"\n"} .. }*} "\n" };

        IdentStart = {'a'..='z'} | {'A'..='Z'} | "_";
        IdentCont = IdentStart | {'0'..='9'};
        NotIdent = { {!'a'..='z'} {!'A'..='Z'} {!"_"} {!'0'..='9'} };
        Ident = { IdentStart {IdentCont*} NotIdent };

        StrLit = { "\"" {{ { {!"\\"} {!"\""} .. } | { "\\" Escape } }*} "\"" };
        CharLit = { "'" { { {!"\\"} {!"'"} .. } | { "\\" Escape } } "'" };
        Escape = "t" | "n" | "r" | "\\" | "'" | "\"";
    };

    grammar.extend(
        grammar!{
            // Main grammar.
            Grammar = { {Shebang?} {rules:{RuleDef*}} Whitespace };
            RuleDef = { {name:Ident} "=" {rule:Or} ";" };
            Or = {rules:{Concat+ % "|"}};
            Concat = {rules:{Rule+}};
            Rule = { {{ {field:Ident} ":" }?} {rule:Primary} {{modifier:Modifier}?} };
            Primary =
                {Eat:Pattern} |
                {NegativeLookahead:{ "!" {pat:Pattern} }} |
                {Call:Ident} |
                {Group:{ "{" {{or:Or}?} "}" }};
            Modifier =
                {Opt:"?"} |
                {Repeat:{ {repeat:Repeat} {{ "%" {sep:Primary} }?} }};
            Repeat =
                {Many:"*"} |
                {More:"+"};
            Pattern =
                {Str:StrLit} |
                {CharRange:{ {{start:CharLit}?} ".." {{end:CharLit}?} }} |
                {CharRangeInclusive:{ {{start:CharLit}?} "..=" {end:CharLit} }};
        }
        .insert_whitespace(grammar::call("Whitespace")),
    );

    fs::write(&out_dir.join("parse_grammar.rs"), grammar.generate_rust()).unwrap();
}