Last active
August 29, 2015 14:21
-
-
Save agmike/0ae5ffd666d2f76b7551 to your computer and use it in GitHub Desktop.
This file contains 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
extern crate parser_combinators; | |
use parser_combinators::*; | |
#[derive(Debug)] | |
struct IncludeDecl { pub file: String } | |
#[derive(Debug)] | |
struct ClassDecl { pub name: String } | |
// Problems: | |
// returning closure: make_parser<I, O, F>() -> F | |
// where F: FnMut(State<I>) -> ParseResult<O, I> | |
// , I: Stream | |
// cloning closures | |
// Matches a token and consumes trailing space | |
macro_rules! token { | |
($e:expr) => ( ($e).skip(spaces()) ) | |
} | |
// Matches an identifier ([_a-z][_a-z0-9]) and consumes trailing space | |
macro_rules! ident { | |
() => ( | |
letter().or(char('_')).then( | |
// TODO: more efficient handling w/o allocation? | |
|c| { many(alpha_num()).map(move |s: String| { c.to_string() + &s }) } | |
).skip(spaces()) | |
) | |
} | |
macro_rules! constant_string { | |
() => ( { | |
let simple_char = satisfy(|c| c != '"' && c != '\\'); | |
let escape_quote = try(string("\\\"").map(|_| '\"')); | |
token!(between(char('"'), char('"'), | |
many(simple_char.or(escape_quote)).map(|s: String| s) | |
)) | |
} ) | |
} | |
// include_list -> include_declaration | |
// include_list -> include_list include_declaration | |
// include_declaration -> KEYWORD_INCLUDE include_file | |
// include_file -> CONSTANT_STRING | |
macro_rules! include_file { | |
() => ( constant_string!() ) | |
} | |
macro_rules! kw_include { | |
() => ( token!(string("include")) ) | |
} | |
macro_rules! include_decl { | |
() => ( | |
kw_include!().with( | |
include_file!().map(|s| IncludeDecl { file: s }) | |
) | |
) | |
} | |
// class_declaration_list -> class_declaration | |
// class_declaration_list -> class_declaration_list class_declaration | |
// class_declaration -> KEYWORD_CLASS identifier '{' '}' ';' | |
// class_declaration -> KEYWORD_CLASS identifier KEYWORD_ISCLASS class_list '{' '}' ';' | |
// class_declaration -> declaration_specifiers KEYWORD_CLASS identifier '{' '}' ';' | |
// class_declaration -> declaration_specifiers KEYWORD_CLASS identifier KEYWORD_ISCLASS class_list '{' '}' ';' | |
// class_declaration -> KEYWORD_CLASS identifier '{' declaration_list '}' ';' | |
// class_declaration -> KEYWORD_CLASS identifier KEYWORD_ISCLASS class_list '{' declaration_list '}' ';' | |
// class_declaration -> declaration_specifiers KEYWORD_CLASS identifier '{' declaration_list '}' ';' | |
// class_declaration -> declaration_specifiers KEYWORD_CLASS identifier KEYWORD_ISCLASS class_list '{' declaration_list '}' ';' | |
// class_list -> identifier | |
// class_list -> class_list ',' identifier | |
macro_rules! declaration_list { | |
() => ( value(Vec::<String>::new()) ) | |
} | |
macro_rules! class_list { | |
() => ( sep_by(ident!(), token!(char(','))).map(|x: Vec<_>| x) ) | |
} | |
macro_rules! class_decl { | |
() => ( { | |
token!(string("class")).with(ident!()).then(|class_name| { | |
optional(token!(string("isclass")).with(class_list!())).then(|base_classes| { | |
token!(char(';')) | |
// Parser::or( token!(char(';')).map(|_| None) | |
// , between( token!(char('{')) | |
// , token!(char('}')) | |
// , declaration_list!() | |
// ).skip(token!(char(';'))).map(Option::Some) | |
// ) | |
}) | |
}) | |
} ) | |
} | |
// program -> include_list class_declaration_list | |
// program -> class_declaration_list | |
macro_rules! include_list { | |
() => ( many(include_decl!()).map(|x: Vec<_>| x) ) | |
} | |
macro_rules! class_decl_list { | |
() => ( many1(class_decl!()).map(|x: Vec<_>| x) ) | |
} | |
macro_rules! program { | |
() => { | |
spaces().with(include_list!().and(class_decl_list!())) | |
} | |
} | |
pub fn parse() { | |
let src = r#" | |
include "gs.gs" | |
include "locomotive.gs" | |
class Test; { | |
public void DoIt() { | |
Interface.Exception("Hello, world!"); | |
} | |
}; | |
"#; | |
//let mut program = program!(); | |
// Expanded version of the above line | |
let mut program = | |
spaces() | |
.with(many( | |
// keyword 'include' | |
(string("include")).skip(spaces()).with({ | |
// string literal | |
let simple_char = satisfy(|c| c != '\"' && c != '\\'); | |
let escape_quote = try (string("\\n").map( | _ | '\n')); | |
(between(char('\"'), char('\"'), many(simple_char.or(escape_quote)).map(|s: String| s))).skip(spaces()) | |
}.map(|s| IncludeDecl { file: s, })) | |
).map(|x: Vec<_>| x) | |
.and(many1({ | |
// keyword 'class' | |
(string("class")).skip(spaces()) | |
// identifier | |
.with(letter().or(char('_')).then(|c| { | |
many(alpha_num()).map(move |s: String| { c.to_string() + &s }) | |
}).skip(spaces())) | |
.then(|class_name| { | |
// keyword 'isclass' | |
optional((string("isclass")).skip(spaces()) | |
// class_list | |
.with(sep_by( | |
// identifier | |
letter().or(char('_')).then(|c| { | |
many(alpha_num()).map(move |s: String| { c.to_string() + &s }) | |
}).skip(spaces()), | |
char(',').skip(spaces()) | |
) | |
.map(|x: Vec<_>| x)) | |
).then(|base_classes| { | |
char(';').skip(spaces()) | |
}) | |
}) | |
}).map(|x: Vec<_>| x))); | |
let result = program.parse(src); | |
match result { | |
Ok(((incls, classes), _)) => println!("Success: {:?} {:?}", incls, classes), | |
Err(e) => println!("{}", e) | |
} | |
} | |
// $ rustc /home/m1ke/src/schemegen/gamescript_syntax/src/lib.rs --crate-name gamescript_syntax --crate-type lib -g -C metadata=f30b929bc547d1a3 -C extra-filename=-f30b929bc547d1a3 --out-dir /home/m1ke/src/schemegen/schemegen/target/debug/deps --emit=dep-info,link -L dependency=/home/m1ke/src/schemegen/schemegen/target/debug/deps -L dependency=/home/m1ke/src/schemegen/schemegen/target/debug/deps --extern parser_combinators=/home/m1ke/src/schemegen/schemegen/target/debug/deps/libparser_combinators-11418352b9356529.rlib -Z time-passes | |
// time: 0.003 parsing | |
// time: 0.001 configuration 1 | |
// time: 0.000 recursion limit | |
// time: 0.000 gated macro checking | |
// time: 0.000 crate injection | |
// time: 0.012 macro loading | |
// time: 0.000 plugin loading | |
// time: 0.000 plugin registration | |
// time: 0.005 expansion | |
// time: 0.000 complete gated feature checking 1 | |
// time: 0.001 configuration 2 | |
// time: 0.000 maybe building test harness | |
// time: 0.000 prelude injection | |
// time: 0.000 checking that all macro invocations are gone | |
// time: 0.000 complete gated feature checking 2 | |
// time: 0.000 assigning node ids and indexing ast | |
// time: 0.000 external crate/lib resolution | |
// time: 0.000 language item collection | |
// time: 0.004 resolution | |
// time: 0.000 lifetime resolution | |
// time: 0.000 looking for entry point | |
// time: 0.000 looking for plugin registrar | |
// time: 0.000 region resolution | |
// time: 0.000 loop checking | |
// time: 0.000 static item recursion checking | |
// time: 0.001 type collecting | |
// time: 0.000 variance inference | |
// time: 0.006 coherence checking | |
// time: 179.359 type checking | |
// time: 0.236 const checking | |
// time: 0.057 privacy checking | |
// time: 0.000 stability index | |
// time: 0.017 intrinsic checking | |
// time: 0.000 effect checking | |
// time: 0.057 match checking | |
// time: 0.001 liveness checking | |
// time: 0.270 borrow checking | |
// time: 0.067 rvalue checking | |
// time: 0.000 reachability checking | |
// time: 0.000 death checking | |
// time: 0.065 stability checking | |
// time: 0.000 unused lib feature checking | |
// time: 0.384 lint checking | |
// time: 0.000 resolving dependency formats | |
// time: 516.564 translation | |
// time: 0.090 llvm function passes | |
// time: 0.031 llvm module passes | |
// time: 1.104 codegen passes | |
// time: 0.001 codegen passes | |
// time: 2.155 LLVM passes | |
// time: 0.236 linking |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment