1#![recursion_limit = "192"]
2
3#[macro_use]
4extern crate quote;
5extern crate codemap;
6extern crate codemap_diagnostic;
7
8use std::io;
9use std::io::prelude::*;
10use std::path::{Path, PathBuf};
11use std::convert::AsRef;
12use std::fs::File;
13use std::process::exit;
14use std::env;
15
16use codemap::{ CodeMap, Span };
17use codemap_diagnostic::{ Diagnostic, Level, SpanLabel, SpanStyle, Emitter, ColorConfig };
18
19mod translate;
20mod grammar;
21
22struct PegCompiler {
23 codemap: CodeMap,
24 diagnostics: Vec<codemap_diagnostic::Diagnostic>
25}
26
27impl PegCompiler {
28 fn new() -> PegCompiler {
29 PegCompiler {
30 codemap: CodeMap::new(),
31 diagnostics: vec![],
32 }
33 }
34
35 fn has_error(&self) -> bool {
36 self.diagnostics.iter().any(|d| d.level == Level::Error || d.level == Level::Bug)
37 }
38
39 fn span_error(&mut self, error: String, span: Span, label: Option<String>) {
40 self.diagnostics.push(Diagnostic {
41 level: Level::Error,
42 message: error,
43 code: None,
44 spans: vec![SpanLabel { span, label, style: SpanStyle::Primary }]
45 });
46 }
47
48 fn span_warning(&mut self, error: String, span: Span, label: Option<String>) {
49 self.diagnostics.push(Diagnostic {
50 level: Level::Warning,
51 message: error,
52 code: None,
53 spans: vec![SpanLabel { span, label, style: SpanStyle::Primary }]
54 });
55 }
56
57 fn print_diagnostics(&mut self) {
58 if !self.diagnostics.is_empty() {
59 let mut emitter = Emitter::stderr(ColorConfig::Auto, Some(&self.codemap));
60 emitter.emit(&self.diagnostics[..]);
61 self.diagnostics.clear();
62 }
63 }
64
65 fn compile(&mut self, filename: String, input: String) -> Result<String, ()> {
66 let file = self.codemap.add_file(filename, input);
67
68 let ast_items = match grammar::items(&file.source(), file.span) {
69 Ok(g) => g,
70 Err(e) => {
71 self.span_error(
72 "Error parsing language specification".to_owned(),
73 file.span.subspan(e.offset as u64, e.offset as u64),
74 Some(format!("{}", e))
75 );
76 return Err(())
77 }
78 };
79
80 let grammar_def = translate::Grammar::from_ast(self, ast_items)?;
81 let output_tokens = translate::compile_grammar(self, &grammar_def);
82
83 if self.has_error() {
84 Err(())
85 } else {
86 Ok(output_tokens?.to_string())
87 }
88 }
89}
90
91pub fn compile(filename: String, input: String) -> Result<String, ()> {
93 let mut compiler = PegCompiler::new();
94 let result = compiler.compile(filename, input);
95 compiler.print_diagnostics();
96 result
97}
98
99pub fn cargo_build<T: AsRef<Path> + ?Sized>(input_path: &T) {
102 let mut stderr = io::stderr();
103 let input_path = input_path.as_ref();
104
105 let mut peg_source = String::new();
106 if let Err(e) = File::open(input_path).and_then(|mut x| x.read_to_string(&mut peg_source)) {
107 writeln!(stderr, "Could not read PEG input file `{}`: {}", input_path.display(), e).unwrap();
108 exit(1);
109 }
110
111 println!("cargo:rerun-if-changed={}", input_path.display());
112
113 let mut compiler = PegCompiler::new();
114 let result = compiler.compile(input_path.to_string_lossy().into_owned(), peg_source);
115 compiler.print_diagnostics();
116
117 let rust_source = match result {
118 Ok(s) => s,
119 Err(()) => {
120 writeln!(stderr, "Error compiling PEG grammar").unwrap();
121 exit(1);
122 }
123 };
124
125 let out_dir: PathBuf = env::var_os("OUT_DIR").unwrap().into();
126 let rust_path = out_dir.join(input_path.file_name().unwrap()).with_extension("rs");
127
128 let mut output_file = File::create(&rust_path).unwrap();
129 output_file.write_all(rust_source.as_bytes()).unwrap();
130}