Skip to content

Commit 6c21e0f

Browse files
author
Nathan Ringo
committed
Implements call-by-name properly.
1 parent 8be4005 commit 6c21e0f

File tree

5 files changed

+117
-105
lines changed

5 files changed

+117
-105
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ authors = ["Nathan Ringo <[email protected]>"]
99
description = "A simple term-rewriting interpreter that displays intermediate expressions."
1010
license = "Apache-2.0/MIT"
1111
name = "evaltrees"
12-
version = "0.0.1"
12+
version = "0.1.0-pre"
1313

1414
[dependencies]
1515
display_attr = "0.1.1"

src/eval/name.rs

Lines changed: 106 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use failure::Error;
2+
use symbol::Symbol;
23

34
use ast::{Decl, Expr, Literal, Op, PrintStyle};
4-
use eval::util::{apply, beta_number, reducible};
5+
use eval::util::{apply, arg_normal_enough, beta_number, reducible};
56
use eval::Evaluator;
67

78
/// Call-by-name evaluation.
@@ -60,15 +61,24 @@ fn step<Aux: Clone>(expr: Expr<Aux>, decls: &[Decl<Aux>]) -> Result<Expr<Aux>, E
6061
Expr::Op(Op::App, l, r, aux) => match beta {
6162
Some(n) if n > 0 => Expr::Op(Op::App, l, r, aux),
6263
Some(0) => {
63-
debug!("rip strictness analysis");
6464
let mut args = vec![*r];
6565
let mut func = *l;
66-
while let Expr::Op(Op::App, f, a, _) = func {
66+
let mut r_types = vec![aux];
67+
while let Expr::Op(Op::App, f, a, ty) = func {
6768
args.push(*a);
6869
func = *f;
70+
r_types.push(ty);
6971
}
7072
args.reverse();
71-
apply(func, args, decls)?
73+
let func_name = match func {
74+
Expr::Variable(var, _) => var,
75+
func => panic!("Invalid callable expression: {}", func),
76+
};
77+
if let Some(n) = check_arg_normalization(func_name, &args, decls) {
78+
normalize_arg(n, func, args, r_types, decls)?
79+
} else {
80+
apply(func_name, args, decls)?
81+
}
7282
}
7383
_ => Expr::Op(Op::App, Box::new(step(*l, decls)?), r, aux),
7484
},
@@ -97,6 +107,19 @@ fn step<Aux: Clone>(expr: Expr<Aux>, decls: &[Decl<Aux>]) -> Result<Expr<Aux>, E
97107
Ok(expr)
98108
}
99109

110+
fn check_arg_normalization<Aux: Clone>(
111+
func: Symbol,
112+
args: &[Expr<Aux>],
113+
decls: &[Decl<Aux>],
114+
) -> Option<usize> {
115+
for (i, a) in args.iter().enumerate() {
116+
if !arg_normal_enough(a, i, func, decls) {
117+
return Some(i);
118+
}
119+
}
120+
None
121+
}
122+
100123
fn math_op<Aux: Clone, F: Fn(usize, usize) -> Result<usize, Error>>(
101124
op: Op,
102125
l: Box<Expr<Aux>>,
@@ -116,30 +139,52 @@ fn math_op<Aux: Clone, F: Fn(usize, usize) -> Result<usize, Error>>(
116139
}
117140
}
118141

142+
fn normalize_arg<Aux: Clone>(
143+
n: usize,
144+
func: Expr<Aux>,
145+
mut args: Vec<Expr<Aux>>,
146+
mut r_types: Vec<Aux>,
147+
decls: &[Decl<Aux>],
148+
) -> Result<Expr<Aux>, Error> {
149+
assert_eq!(args.len(), r_types.len());
150+
args.reverse();
151+
let mut out = func;
152+
let mut i = 0;
153+
while let Some(arg) = args.pop() {
154+
let arg = if i == n { step(arg, decls)? } else { arg };
155+
i += 1;
156+
out = Expr::Op(
157+
Op::App,
158+
Box::new(out),
159+
Box::new(arg),
160+
r_types.pop().unwrap(),
161+
);
162+
}
163+
assert_eq!(n, 0);
164+
Ok(out)
165+
}
166+
119167
#[cfg(test)]
120168
mod tests {
121169
use super::*;
122-
use ast::{Pattern, Type};
170+
use ast::Pattern;
123171

124172
#[test]
125173
fn app() {
126174
let mut evaluator = CallByName::new(vec![
127175
Decl {
128176
name: "f".into(),
129177
args: vec![
130-
Pattern::Binding("x".into(), Type::Int),
131-
Pattern::Binding("y".into(), Type::Int),
178+
Pattern::Binding("x".into(), ()),
179+
Pattern::Binding("y".into(), ()),
132180
],
133181
body: Expr::Op(
134182
Op::Add,
135-
Box::new(Expr::Variable("x".into(), Type::Int)),
136-
Box::new(Expr::Variable("y".into(), Type::Int)),
137-
Type::Int,
138-
),
139-
aux: Type::Func(
140-
Box::new(Type::Int),
141-
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
183+
Box::new(Expr::Variable("x".into(), ())),
184+
Box::new(Expr::Variable("y".into(), ())),
185+
(),
142186
),
187+
aux: (),
143188
},
144189
Decl {
145190
name: "".into(),
@@ -148,30 +193,24 @@ mod tests {
148193
Op::App,
149194
Box::new(Expr::Op(
150195
Op::App,
151-
Box::new(Expr::Variable(
152-
"f".into(),
153-
Type::Func(
154-
Box::new(Type::Int),
155-
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
156-
),
157-
)),
196+
Box::new(Expr::Variable("f".into(), ())),
158197
Box::new(Expr::Op(
159198
Op::Add,
160-
Box::new(Expr::Literal(Literal::Int(1), Type::Int)),
161-
Box::new(Expr::Literal(Literal::Int(2), Type::Int)),
162-
Type::Int,
199+
Box::new(Expr::Literal(Literal::Int(1), ())),
200+
Box::new(Expr::Literal(Literal::Int(2), ())),
201+
(),
163202
)),
164-
Type::Int,
203+
(),
165204
)),
166205
Box::new(Expr::Op(
167206
Op::Add,
168-
Box::new(Expr::Literal(Literal::Int(3), Type::Int)),
169-
Box::new(Expr::Literal(Literal::Int(4), Type::Int)),
170-
Type::Int,
207+
Box::new(Expr::Literal(Literal::Int(3), ())),
208+
Box::new(Expr::Literal(Literal::Int(4), ())),
209+
(),
171210
)),
172-
Type::Int,
211+
(),
173212
),
174-
aux: Type::Int,
213+
aux: (),
175214
},
176215
]);
177216

@@ -205,68 +244,50 @@ mod tests {
205244
Decl {
206245
name: "f".into(),
207246
args: vec![
208-
Pattern::Literal(Literal::Nil, Type::List(Box::new(Type::Int))),
209-
Pattern::Binding("y".into(), Type::Int),
247+
Pattern::Literal(Literal::Nil, ()),
248+
Pattern::Binding("y".into(), ()),
210249
],
211-
body: Expr::Variable("y".into(), Type::Int),
212-
aux: Type::Func(
213-
Box::new(Type::List(Box::new(Type::Int))),
214-
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
215-
),
250+
body: Expr::Variable("y".into(), ()),
251+
aux: (),
216252
},
217253
Decl {
218254
name: "f".into(),
219255
args: vec![
220256
Pattern::Cons(
221-
Box::new(Pattern::Binding("h".into(), Type::Int)),
222-
Box::new(Pattern::Binding("t".into(), Type::Int)),
223-
Type::List(Box::new(Type::Int)),
257+
Box::new(Pattern::Binding("h".into(), ())),
258+
Box::new(Pattern::Binding("t".into(), ())),
259+
(),
224260
),
225-
Pattern::Binding("y".into(), Type::Int),
261+
Pattern::Binding("y".into(), ()),
226262
],
227263
body: Expr::Op(
228264
Op::Add,
229-
Box::new(Expr::Variable("h".into(), Type::Int)),
265+
Box::new(Expr::Variable("h".into(), ())),
230266
Box::new(Expr::Op(
231267
Op::App,
232268
Box::new(Expr::Op(
233269
Op::App,
234-
Box::new(Expr::Variable(
235-
"f".into(),
236-
Type::Func(
237-
Box::new(Type::List(Box::new(Type::Int))),
238-
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
239-
),
240-
)),
241-
Box::new(Expr::Variable("t".into(), Type::Int)),
242-
Type::Func(Box::new(Type::Int), Box::new(Type::Int)),
270+
Box::new(Expr::Variable("f".into(), ())),
271+
Box::new(Expr::Variable("t".into(), ())),
272+
(),
243273
)),
244-
Box::new(Expr::Variable("y".into(), Type::Int)),
245-
Type::Int,
274+
Box::new(Expr::Variable("y".into(), ())),
275+
(),
246276
)),
247-
Type::Func(
248-
Box::new(Type::List(Box::new(Type::Int))),
249-
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
250-
),
251-
),
252-
aux: Type::Func(
253-
Box::new(Type::List(Box::new(Type::Int))),
254-
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
277+
(),
255278
),
279+
aux: (),
256280
},
257281
Decl {
258282
name: "g".into(),
259-
args: vec![Pattern::Binding("x".into(), Type::Int)],
283+
args: vec![Pattern::Binding("x".into(), ())],
260284
body: Expr::Op(
261285
Op::Cons,
262-
Box::new(Expr::Variable("x".into(), Type::Int)),
263-
Box::new(Expr::Literal(Literal::Nil, Type::List(Box::new(Type::Int)))),
264-
Type::List(Box::new(Type::Int)),
265-
),
266-
aux: Type::Func(
267-
Box::new(Type::Int),
268-
Box::new(Type::List(Box::new(Type::Int))),
286+
Box::new(Expr::Variable("x".into(), ())),
287+
Box::new(Expr::Literal(Literal::Nil, ())),
288+
(),
269289
),
290+
aux: (),
270291
},
271292
Decl {
272293
name: "".into(),
@@ -275,41 +296,29 @@ mod tests {
275296
Op::App,
276297
Box::new(Expr::Op(
277298
Op::App,
278-
Box::new(Expr::Variable(
279-
"f".into(),
280-
Type::Func(
281-
Box::new(Type::List(Box::new(Type::Int))),
282-
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
283-
),
284-
)),
299+
Box::new(Expr::Variable("f".into(), ())),
285300
Box::new(Expr::Op(
286301
Op::App,
287-
Box::new(Expr::Variable(
288-
"g".into(),
289-
Type::Func(
290-
Box::new(Type::Int),
291-
Box::new(Type::List(Box::new(Type::Int))),
292-
),
293-
)),
302+
Box::new(Expr::Variable("g".into(), ())),
294303
Box::new(Expr::Op(
295304
Op::Add,
296-
Box::new(Expr::Literal(Literal::Int(1), Type::Int)),
297-
Box::new(Expr::Literal(Literal::Int(2), Type::Int)),
298-
Type::Int,
305+
Box::new(Expr::Literal(Literal::Int(1), ())),
306+
Box::new(Expr::Literal(Literal::Int(2), ())),
307+
(),
299308
)),
300-
Type::List(Box::new(Type::Int)),
309+
(),
301310
)),
302-
Type::Func(Box::new(Type::Int), Box::new(Type::Int)),
311+
(),
303312
)),
304313
Box::new(Expr::Op(
305314
Op::Add,
306-
Box::new(Expr::Literal(Literal::Int(3), Type::Int)),
307-
Box::new(Expr::Literal(Literal::Int(4), Type::Int)),
308-
Type::Int,
315+
Box::new(Expr::Literal(Literal::Int(3), ())),
316+
Box::new(Expr::Literal(Literal::Int(4), ())),
317+
(),
309318
)),
310-
Type::Int,
319+
(),
311320
),
312-
aux: Type::Int,
321+
aux: (),
313322
},
314323
]);
315324

@@ -335,7 +344,10 @@ mod tests {
335344
assert!(!evaluator.normal_form());
336345

337346
assert!(evaluator.step().is_ok());
338-
assert_eq!(format!("{}", evaluator), "Add(Add(1, 2), Add(3, 4))");
347+
assert_eq!(
348+
format!("{}", evaluator),
349+
"Add(3, App(App(f, []), Add(3, 4)))"
350+
);
339351
assert!(!evaluator.normal_form());
340352

341353
assert!(evaluator.step().is_ok());

src/eval/util.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,10 @@ pub fn beta_number<Aux>(expr: &Expr<Aux>, decls: &[Decl<Aux>]) -> Option<isize>
6363

6464
/// Performs "normal" (for call-by-name and call-by-value) function application.
6565
pub fn apply<Aux: Clone>(
66-
func: Expr<Aux>,
66+
func: Symbol,
6767
args: Vec<Expr<Aux>>,
6868
decls: &[Decl<Aux>],
6969
) -> Result<Expr<Aux>, Error> {
70-
let func = match func {
71-
Expr::Variable(var, _) => var,
72-
func => panic!("Invalid callable expression: {}", func),
73-
};
7470
let (decl, args) = decls
7571
.iter()
7672
.filter(|decl| decl.name == func)
@@ -80,8 +76,8 @@ pub fn apply<Aux: Clone>(
8076
Ok(apply_replacement(&decl.body, &args))
8177
}
8278

83-
/// Returns whether the given expression is normalized enough to be a valid n-from-the-right-th
84-
/// argument to the decl with the given name. Used in call-by-name and lazy evaluation.
79+
/// Returns whether the given expression is normalized enough to be a valid nth argument to the
80+
/// decl with the given name. Used in call-by-name and lazy evaluation.
8581
pub fn arg_normal_enough<Aux: Clone>(
8682
value: &Expr<Aux>,
8783
n: usize,
@@ -91,7 +87,7 @@ pub fn arg_normal_enough<Aux: Clone>(
9187
let pats = decls
9288
.iter()
9389
.filter(|decl| decl.name == name)
94-
.map(|decl| &decl.args[decl.args.len() - n]);
90+
.map(|decl| &decl.args[n]);
9591
for pat in pats {
9692
if arg_normal_enough_for_pat(value, pat, decls) {
9793
if pat.matches(value).is_some() {

src/eval/value.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ fn step<Aux: Clone>(expr: Expr<Aux>, decls: &[Decl<Aux>]) -> Result<Expr<Aux>, E
7373
func = *f;
7474
}
7575
args.reverse();
76+
let func = match func {
77+
Expr::Variable(var, _) => var,
78+
func => panic!("Invalid callable expression: {}", func),
79+
};
7680
apply(func, args, decls)?
7781
}
7882
_ => Expr::Op(Op::App, Box::new(step(*l, decls)?), r, aux),

0 commit comments

Comments
 (0)