1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::sync::RwLock;
use std::collections::HashMap;
use std::error::Error as StdError;
use base::ast;
use base::ast::MutVisitor;
use base::types::TcIdent;
use base::error::Errors;
use thread::Thread;
pub type Error = Box<StdError + Send + Sync>;
pub trait Macro: ::mopa::Any + Send + Sync {
fn expand(&self,
env: &Thread,
arguments: &mut [ast::LExpr<TcIdent>])
-> Result<ast::LExpr<TcIdent>, Error>;
fn clone(&self) -> Box<Macro>;
}
mopafy!(Macro);
impl<F: ::mopa::Any + Clone + Send + Sync> Macro for F
where F: Fn(&Thread, &mut [ast::LExpr<TcIdent>]) -> Result<ast::LExpr<TcIdent>, Error>
{
fn expand(&self,
env: &Thread,
arguments: &mut [ast::LExpr<TcIdent>])
-> Result<ast::LExpr<TcIdent>, Error> {
self(env, arguments)
}
fn clone(&self) -> Box<Macro> {
Box::new(Clone::clone(self))
}
}
pub struct MacroEnv {
macros: RwLock<HashMap<String, Box<Macro>>>,
}
impl MacroEnv {
pub fn new() -> MacroEnv {
MacroEnv { macros: RwLock::new(HashMap::new()) }
}
pub fn insert<M>(&self, name: String, mac: M)
where M: Macro + 'static
{
self.macros.write().unwrap().insert(name, Box::new(mac));
}
pub fn get(&self, name: &str) -> Option<Box<Macro>> {
self.macros.read().unwrap().get(name).map(|x| (**x).clone())
}
pub fn run(&self, env: &Thread, expr: &mut ast::LExpr<TcIdent>) -> Result<(), Errors<Error>> {
let mut expander = MacroExpander {
env: env,
macros: self,
errors: Errors::new(),
};
expander.visit_expr(expr);
if expander.errors.has_errors() {
Err(expander.errors)
} else {
Ok(())
}
}
}
struct MacroExpander<'a> {
env: &'a Thread,
macros: &'a MacroEnv,
errors: Errors<Error>,
}
impl<'a> MutVisitor for MacroExpander<'a> {
type T = TcIdent;
fn visit_expr(&mut self, expr: &mut ast::LExpr<TcIdent>) {
let replacement = match expr.value {
ast::Expr::Call(ref mut id, ref mut args) => {
match ***id {
ast::Expr::Identifier(ref id) => {
match self.macros.get(id.name.as_ref()) {
Some(m) => {
match m.expand(self.env, args) {
Ok(e) => Some(e),
Err(err) => {
self.errors.error(err);
None
}
}
}
None => None,
}
}
_ => None,
}
}
_ => None,
};
if let Some(mut e) = replacement {
e.location = expr.location;
*expr = e;
}
ast::walk_mut_expr(self, expr);
}
}