Skip to content

Commit c5a9fc3

Browse files
committed
Initial lowering for ExpressionLetIn
1 parent f105bf1 commit c5a9fc3

14 files changed

+420
-18
lines changed

crates/lowering/src/algorithm.rs

Lines changed: 174 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use std::iter;
22

33
use indexing::{ExprItem, ExprItemId, IndexingResult, ValueGroupId};
4-
use rowan::ast::AstNode;
4+
use rowan::ast::{AstNode, AstPtr};
5+
use rustc_hash::FxHashMap;
56
use smol_str::SmolStr;
67
use syntax::cst;
78

89
use crate::{
9-
BinderId, BinderKind, ExpressionArgument, ExpressionId, ExpressionKind, LoweredEquation,
10-
LoweredExprItem, LoweringMap, LoweringResult, OperatorPair, SourceMap, TickPair, TypeId,
11-
TypeKind,
10+
BinderId, BinderKind, ExpressionArgument, ExpressionId, ExpressionKind, LetBindingId,
11+
LetBindingKind, LetBindingKindId, LoweredEquation, LoweredExprItem, LoweringMap,
12+
LoweringResult, OperatorPair, SourceMap, TickPair, TypeId, TypeKind,
1213
};
1314

1415
#[derive(Default)]
@@ -90,17 +91,7 @@ fn lower_value_group(
9091
let Some(ptr) = index.source_map.declaration_ptr(id) else { continue };
9192
let Some(node) = ptr.try_to_node(root) else { continue };
9293
let cst::Declaration::ValueEquation(e) = node else { continue };
93-
94-
if let Some(f) = e.function_binders() {
95-
for b in f.children() {
96-
equation.binders.push(lower_binder(state, &b));
97-
}
98-
}
99-
100-
let Some(g) = e.guarded_expression() else { continue };
101-
let Some(w) = g.where_expression() else { continue };
102-
let Some(e) = w.expression() else { continue };
103-
equation.expression = Some(lower_expression(state, &e));
94+
lower_equation_like(state, equation, e.function_binders(), e.guarded_expression());
10495
}
10596

10697
LoweredExprItem::Value { signature, equations }
@@ -229,7 +220,11 @@ fn lower_expression(state: &mut State, cst: &cst::Expression) -> ExpressionId {
229220
});
230221
ExpressionKind::IfThenElse { r#if, then, r#else }
231222
}
232-
cst::Expression::ExpressionLetIn(_l) => ExpressionKind::LetIn,
223+
cst::Expression::ExpressionLetIn(l) => {
224+
let bindings = l.bindings().map_or(vec![], |b| lower_let_binding_statements(state, &b));
225+
let expression = l.expression().map(|e| lower_expression(state, &e));
226+
ExpressionKind::LetIn { bindings, expression }
227+
}
233228
cst::Expression::ExpressionLambda(_l) => ExpressionKind::Lambda,
234229
cst::Expression::ExpressionCaseOf(_c) => ExpressionKind::CaseOf,
235230
cst::Expression::ExpressionDo(_d) => ExpressionKind::Do,
@@ -253,3 +248,166 @@ fn lower_expression(state: &mut State, cst: &cst::Expression) -> ExpressionId {
253248
};
254249
state.source_map.insert_expression(cst, kind)
255250
}
251+
252+
fn lower_let_binding_statements(
253+
state: &mut State,
254+
cst: &cst::LetBindingStatements,
255+
) -> Vec<LetBindingId> {
256+
let mut nominal = FxHashMap::default();
257+
cst.children().map(|b| lower_let_binding(state, &mut nominal, &b)).collect()
258+
}
259+
260+
fn lower_let_binding(
261+
state: &mut State,
262+
nominal: &mut FxHashMap<SmolStr, LetBindingKindId>,
263+
cst: &cst::LetBinding,
264+
) -> LetBindingId {
265+
fn insert_kind_id(
266+
state: &mut State,
267+
ptr: &cst::LetBinding,
268+
kind_id: LetBindingKindId,
269+
) -> LetBindingId {
270+
let ptr = AstPtr::new(ptr);
271+
let (index, _) = state.source_map.let_bindings.insert_full(ptr, kind_id);
272+
LetBindingId::from_raw(index)
273+
}
274+
275+
fn insert_kind(
276+
state: &mut State,
277+
ptr: &cst::LetBinding,
278+
kind: LetBindingKind,
279+
) -> (LetBindingKindId, LetBindingId) {
280+
let index = state.source_map.let_bindings_grouped.len();
281+
let kind_id = LetBindingKindId::from_raw(index);
282+
state.source_map.let_bindings_grouped.push(kind);
283+
let ptr_id = insert_kind_id(state, ptr, kind_id);
284+
(kind_id, ptr_id)
285+
}
286+
287+
match cst {
288+
cst::LetBinding::LetBindingPattern(p) => {
289+
let kind = lower_let_binding_pattern(state, p);
290+
let (_, ptr_id) = insert_kind(state, cst, kind);
291+
ptr_id
292+
}
293+
cst::LetBinding::LetBindingSignature(s) => {
294+
let token = s.name_token();
295+
let name = token.as_ref().map(|t| t.text());
296+
let signature = s.signature().map(|s| lower_type(state, &s));
297+
298+
if let Some(name) = name {
299+
if let Some(&kind_id) = nominal.get(name) {
300+
let index: usize = kind_id.into();
301+
let group = &mut state.source_map.let_bindings_grouped[index];
302+
if let LetBindingKind::Value { signature: s, .. } = group {
303+
if let Some(existing) = s {
304+
unimplemented!(
305+
"{:?} <-> {:?} : duplicate let bindings",
306+
existing,
307+
signature
308+
);
309+
} else {
310+
*s = signature;
311+
}
312+
}
313+
return insert_kind_id(state, cst, kind_id);
314+
}
315+
}
316+
317+
let name = name.map(SmolStr::from);
318+
let for_nominal = name.clone();
319+
320+
let equations = vec![];
321+
let kind = LetBindingKind::Value { name, signature, equations };
322+
323+
let (kind_id, ptr_id) = insert_kind(state, cst, kind);
324+
325+
if let Some(name) = for_nominal {
326+
nominal.insert(name, kind_id);
327+
}
328+
329+
ptr_id
330+
}
331+
cst::LetBinding::LetBindingEquation(e) => {
332+
let token = e.name_token();
333+
let name = token.as_ref().map(|t| t.text());
334+
335+
let mut equation = LoweredEquation::default();
336+
lower_equation_like(state, &mut equation, e.function_binders(), e.guarded_expression());
337+
338+
if let Some(name) = name {
339+
if let Some(&kind_id) = nominal.get(name) {
340+
let index: usize = kind_id.into();
341+
let group = &mut state.source_map.let_bindings_grouped[index];
342+
if let LetBindingKind::Value { equations, .. } = group {
343+
equations.push(equation);
344+
}
345+
return insert_kind_id(state, cst, kind_id);
346+
}
347+
}
348+
349+
let name = name.map(SmolStr::from);
350+
let for_nominal = name.clone();
351+
352+
let signature = None;
353+
let equations = vec![equation];
354+
let kind = LetBindingKind::Value { name, signature, equations };
355+
356+
let (kind_id, ptr_id) = insert_kind(state, cst, kind);
357+
358+
if let Some(name) = for_nominal {
359+
nominal.insert(name, kind_id);
360+
}
361+
362+
ptr_id
363+
}
364+
}
365+
}
366+
367+
fn lower_let_binding_pattern(state: &mut State, cst: &cst::LetBindingPattern) -> LetBindingKind {
368+
let pattern = cst.binder().map(|b| lower_binder(state, &b));
369+
370+
let where_expression = cst.where_expression();
371+
372+
let expression = where_expression.as_ref().and_then(|w| {
373+
let e = w.expression()?;
374+
Some(lower_expression(state, &e))
375+
});
376+
377+
let bindings = where_expression
378+
.as_ref()
379+
.and_then(|w| {
380+
let b = w.bindings()?;
381+
Some(lower_let_binding_statements(state, &b))
382+
})
383+
.unwrap_or_default();
384+
385+
LetBindingKind::Pattern { pattern, expression, bindings }
386+
}
387+
388+
fn lower_equation_like(
389+
state: &mut State,
390+
equation: &mut LoweredEquation,
391+
function_binders: Option<cst::FunctionBinders>,
392+
guarded_expression: Option<cst::GuardedExpression>,
393+
) {
394+
let where_expression = guarded_expression.and_then(|g| g.where_expression());
395+
396+
let binders = function_binders.map_or(vec![], |b| {
397+
let children = b.children();
398+
children.map(|b| lower_binder(state, &b)).collect()
399+
});
400+
401+
let expression = where_expression.as_ref().and_then(|w| {
402+
let e = w.expression()?;
403+
Some(lower_expression(state, &e))
404+
});
405+
406+
let bindings = where_expression
407+
.and_then(|w| w.bindings())
408+
.map_or(vec![], |b| lower_let_binding_statements(state, &b));
409+
410+
equation.binders = binders;
411+
equation.expression = expression;
412+
equation.bindings = bindings;
413+
}

crates/lowering/src/concrete.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@ pub enum ExpressionArgument {
2424
Type(Option<TypeId>),
2525
}
2626

27+
#[derive(Debug, PartialEq, Eq, Hash)]
28+
pub enum LetBindingKind {
29+
Value {
30+
name: Option<SmolStr>,
31+
signature: Option<TypeId>,
32+
equations: Vec<LoweredEquation>,
33+
},
34+
Pattern {
35+
pattern: Option<BinderId>,
36+
expression: Option<ExpressionId>,
37+
bindings: Vec<LetBindingId>,
38+
},
39+
}
40+
41+
/// A stable ID for lowered let bindings.
42+
///
43+
/// See comments in [`crate::sourcemap::SourceMap`]
44+
pub type LetBindingKindId = Id<LetBindingKind>;
45+
46+
/// A stable ID for an individual let binding.
47+
pub type LetBindingId = Id<cst::LetBinding>;
48+
49+
pub type LetBindingPtr = AstPtr<cst::LetBinding>;
50+
2751
#[derive(Debug, PartialEq, Eq, Hash)]
2852
pub enum ExpressionKind {
2953
Typed {
@@ -50,7 +74,10 @@ pub enum ExpressionKind {
5074
then: Option<ExpressionId>,
5175
r#else: Option<ExpressionId>,
5276
},
53-
LetIn,
77+
LetIn {
78+
bindings: Vec<LetBindingId>,
79+
expression: Option<ExpressionId>,
80+
},
5481
Lambda,
5582
CaseOf,
5683
Do,
@@ -144,6 +171,7 @@ pub struct Binder {
144171
pub struct LoweredEquation {
145172
pub binders: Vec<BinderId>,
146173
pub expression: Option<ExpressionId>,
174+
pub bindings: Vec<LetBindingId>,
147175
}
148176

149177
#[derive(Debug, PartialEq, Eq, Hash)]

crates/lowering/src/sourcemap.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ use rowan::ast::{AstNode, AstPtr};
44
use rustc_hash::FxBuildHasher;
55
use syntax::cst;
66

7+
use crate::{LetBindingKind, LetBindingKindId, LetBindingPtr};
8+
79
use super::{
810
BinderId, BinderKind, BinderPtr, ExpressionId, ExpressionKind, ExpressionPtr, TypeId, TypeKind,
911
TypePtr,
1012
};
1113

1214
type FxIndexMap<K, V> = IndexMap<K, V, FxBuildHasher>;
1315

16+
/// Mapping from recursive trees to stable IDs.
1417
#[derive(Debug, Default, PartialEq, Eq)]
1518
pub struct SourceMap {
1619
types: FxIndexMap<TypePtr, TypeKind>,
1720
binders: FxIndexMap<BinderPtr, BinderKind>,
1821
expressions: FxIndexMap<ExpressionPtr, ExpressionKind>,
22+
// We opt to define insertion logic for the following mappings in
23+
// the lowering code itself because it's non-trivial, thus we mark
24+
// them as pub(crate)
25+
pub(crate) let_bindings: FxIndexMap<LetBindingPtr, LetBindingKindId>,
26+
pub(crate) let_bindings_grouped: Vec<LetBindingKind>,
1927
}
2028

2129
fn insert<K: AstNode, V>(m: &mut FxIndexMap<AstPtr<K>, V>, p: &K, k: V) -> Id<K> {

crates/lowering/tests/concrete.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,21 @@ fn lower_expression_if_then_else() {
7373
let (_, lower) = lower_declaration(["ifThenElse = if a then b else c"]);
7474
insta::assert_debug_snapshot!(&lower.source_map);
7575
}
76+
77+
#[test]
78+
fn lower_expression_let_in() {
79+
let (_, lower) = lower_declaration([
80+
"letIn =",
81+
" let",
82+
" life :: Int",
83+
" life = 42",
84+
"",
85+
" 42 = value",
86+
" where",
87+
" value = 42",
88+
"",
89+
" in",
90+
" life",
91+
]);
92+
insta::assert_debug_snapshot!(&lower.source_map);
93+
}

crates/lowering/tests/snapshots/concrete__lower_expression_application_chain.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,6 @@ SourceMap {
7171
],
7272
},
7373
},
74+
let_bindings: {},
75+
let_bindings_grouped: [],
7476
}

crates/lowering/tests/snapshots/concrete__lower_expression_if_then_else.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,6 @@ SourceMap {
4242
),
4343
},
4444
},
45+
let_bindings: {},
46+
let_bindings_grouped: [],
4547
}

crates/lowering/tests/snapshots/concrete__lower_expression_infix_chain.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,6 @@ SourceMap {
6666
],
6767
},
6868
},
69+
let_bindings: {},
70+
let_bindings_grouped: [],
6971
}

0 commit comments

Comments
 (0)