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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
extern crate pest;
use std::{collections::HashMap, rc::Rc};
use pest::{iterators::Pair, Parser};
use crate::{
binop::{BinOp, Comparison},
scope::ScopePtr,
value::Value,
Error,
};
#[derive(Parser)]
#[grammar = "conflag.pest"]
pub struct ConflagParser;
#[derive(Debug, Clone)]
pub enum AstNode {
Object(HashMap<String, AstNode>),
Patch(Box<AstNode>),
Array(Vec<AstNode>),
Number(f64),
Boolean(bool),
Null,
String(String),
Name(String),
Attribute {
value: Box<AstNode>,
attr: String,
},
FunctionCall {
f: Box<AstNode>,
args: Vec<AstNode>,
},
Lambda {
arg_names: Vec<String>,
expr: Rc<AstNode>,
},
BinOp {
kind: BinOp,
left: Box<AstNode>,
right: Box<AstNode>,
},
}
impl AstNode {
pub fn parse(s: &str) -> Result<AstNode, Error> {
let mut pairs = ConflagParser::parse(Rule::file, s)?;
AstNode::parse_value(pairs.next().unwrap())
}
pub fn value(&self, scope: &ScopePtr) -> Value {
match self {
AstNode::Object(values) => {
let mut new_scope = ScopePtr::new(Some(scope.clone()));
let values = values
.iter()
.map(|(k, v)| (k.clone(), v.clone().value(&new_scope).into()))
.collect();
// Safety: This is the one place we set values on a scope.
// There very possibly _are_ other references to the pointer (eg. functions, names)
// but they are guaranteed to not be setting values also, since no other calls do.
unsafe { new_scope.set_values(values) };
Value::Object(new_scope)
}
AstNode::Array(values) => {
let values = values
.iter()
.map(|v| v.clone().value(scope).into())
.collect();
Value::Array(values)
}
AstNode::Number(n) => Value::Number(*n),
AstNode::Boolean(b) => Value::Boolean(*b),
AstNode::String(s) => Value::String(s.clone()),
AstNode::Null => Value::Null,
AstNode::Name(name) => Value::Name(scope.clone(), name.clone()),
AstNode::Patch(v) => Value::Patch(v.value(scope).into()),
AstNode::Lambda { arg_names, expr } => Value::Lambda {
scope: scope.clone(),
arg_names: arg_names.clone(),
expr: expr.clone(),
},
AstNode::FunctionCall { f, args } => Value::FunctionCall {
f: f.value(scope).into(),
args: args.iter().map(|v| v.clone().value(scope).into()).collect(),
},
AstNode::BinOp { kind, left, right } => Value::BinOp {
kind: *kind,
left: left.value(scope).into(),
right: right.value(scope).into(),
},
AstNode::Attribute { value, attr } => Value::Attribute {
value: value.value(scope).into(),
attr: attr.clone(),
},
}
}
fn parse_name_or_string(pair: Pair<Rule>) -> String {
match pair.as_rule() {
Rule::name => pair,
Rule::string => pair.into_inner().next().unwrap(),
_ => unreachable!("Unexpected rule for parse_name_or_string: {:?}", pair),
}
.as_str()
.into()
}
pub fn parse_value(pair: Pair<Rule>) -> Result<AstNode, Error> {
// println!(
// "PARSING RULE {:?} ({}) IN SCOPE {:?}",
// pair.as_rule(),
// pair.as_str(),
// &scope
// );
let rule = pair.as_rule();
let node = match rule {
Rule::object => {
let pairs: Result<HashMap<String, AstNode>, Error> = pair
.into_inner()
.map(|pair| {
let mut inner_rules = pair.into_inner();
let name = AstNode::parse_name_or_string(inner_rules.next().unwrap());
let value = AstNode::parse_value(inner_rules.next().unwrap());
value.map(|v| (name, v))
})
.collect();
AstNode::Object(pairs?)
}
Rule::array => {
let values: Result<Vec<AstNode>, Error> =
pair.into_inner().map(AstNode::parse_value).collect();
AstNode::Array(values?)
}
Rule::string => {
AstNode::String(String::from(pair.into_inner().next().unwrap().as_str()))
}
Rule::number => AstNode::Number(pair.as_str().parse().unwrap()),
Rule::boolean => AstNode::Boolean(pair.as_str().parse().unwrap()),
Rule::null => AstNode::Null,
Rule::lambda => {
let mut inner_rules = pair.into_inner();
let arg_list = inner_rules.next().unwrap();
let arg_names: Vec<String> = arg_list
.into_inner()
.map(AstNode::parse_name_or_string)
.collect();
let expr = AstNode::parse_value(inner_rules.next().unwrap())?;
// Idea:
// - create a scope whose parent scope is the lexical scope of the function definition
// - when we call the function
// 1 create a new scope whose parent scope is the function's lexical scope and contains argument values
// 2 clone the expression tree, but replace parent pointers to point to this new scope
// 3 evaluate the expression in this new scope
AstNode::Lambda {
arg_names,
expr: Rc::new(expr),
}
}
Rule::atom => {
let mut inner_rules = pair.into_inner();
let mut value = AstNode::parse_value(inner_rules.next().unwrap())?;
for pair in inner_rules {
match pair.as_rule() {
Rule::atom_attribute => {
let attr =
AstNode::parse_name_or_string(pair.into_inner().next().unwrap());
value = AstNode::Attribute {
value: Box::new(value),
attr,
};
}
Rule::atom_function_call => {
// TODO: macro that does something like
// let [f, value_list] = unpack_rule!(inner_rules, 2);
// or maybe something smarter that lets me match against the Pairs object
// and automatically returns an error result if there's a different number of tokens
let mut inner_rules = pair.into_inner();
let expression_list = inner_rules.next().unwrap();
let args: Result<Vec<AstNode>, Error> = expression_list
.into_inner()
.map(AstNode::parse_value)
.collect();
value = AstNode::FunctionCall {
f: Box::new(value),
args: args?,
}
}
_ => unreachable!(),
}
}
value
}
Rule::name => AstNode::Name(AstNode::parse_name_or_string(pair)),
// TODO: operator precedence
Rule::binops => {
let mut inner_rules = pair.into_inner();
let mut left_node = AstNode::parse_value(inner_rules.next().unwrap())?;
while let Some(binop) = inner_rules.next() {
let kind = match binop.as_str() {
"+" => BinOp::Plus,
"-" => BinOp::Subtract,
"*" => BinOp::Multiply,
"/" => BinOp::Divide,
"==" => BinOp::Compare(Comparison::Equal),
"!=" => BinOp::Compare(Comparison::NotEqual),
">" => BinOp::Compare(Comparison::GreaterThan),
">=" => BinOp::Compare(Comparison::GreaterThanOrEqual),
"<" => BinOp::Compare(Comparison::LessThan),
"<=" => BinOp::Compare(Comparison::LessThanOrEqual),
_ => unreachable!("Unexpected binop literal '{}'", binop.as_str()),
};
let right_node = AstNode::parse_value(inner_rules.next().unwrap())?;
left_node = AstNode::BinOp {
kind,
left: Box::new(left_node),
right: Box::new(right_node),
};
}
left_node
}
Rule::patch => AstNode::Patch(Box::new(AstNode::parse_value(
pair.into_inner().next().unwrap(),
)?)),
Rule::patch_map => {
// This is horrible :P Basically we're writing a macro here to replace
// &&x -> &((a) => map((v) => v + &x, a))
let inner = AstNode::parse_value(pair.into_inner().next().unwrap())?;
let map = AstNode::Lambda {
arg_names: vec!["a".into()],
expr: Rc::new(AstNode::FunctionCall {
// TODO: we want this to always be builtin map, it shouldn't be possible to scope-shadow it
f: Box::new(AstNode::Name("map".into())),
args: vec![
AstNode::Lambda {
arg_names: vec!["v".into()],
expr: Rc::new(AstNode::BinOp {
kind: BinOp::Plus,
left: Box::new(AstNode::Name("v".into())),
right: Box::new(AstNode::Patch(Box::new(inner))),
}),
},
AstNode::Name("a".into()),
],
}),
};
AstNode::Patch(Box::new(map))
}
Rule::file
| Rule::EOI
| Rule::pair
| Rule::string_inner
| Rule::char
| Rule::arg_list
| Rule::primitive
| Rule::binop
| Rule::atom_attribute
| Rule::atom_function_call
| Rule::expression
| Rule::expression_list
| Rule::COMMENT
| Rule::WHITESPACE => unreachable!(),
};
Ok(node)
}
}