Skip to content

Commit 696a03e

Browse files
committed
Record and tuple patterns, new pattern match compiler
1 parent 6e2d6a7 commit 696a03e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2298
-873
lines changed

doc/roadmap.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@
3939
- [ ] refinement types
4040
- [x] match expressions
4141
- [x] single-layer pattern matching
42-
- [ ] multi-layer pattern matching
42+
- [x] multi-layer pattern matching
4343
- [ ] dependent pattern matching
44-
- [ ] patterns
44+
- [x] patterns
4545
- [x] wildcard patterns
4646
- [x] named patterns
4747
- [x] annotated patterns
4848
- [x] numeric literal patterns
49-
- [ ] record literal patterns
49+
- [x] record literal patterns
5050
- [ ] invertible format descriptions
5151

5252
## Implementation

fathom/src/core.rs

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
33
use std::fmt;
44

5-
use crate::env::{Index, Level};
5+
use scoped_arena::Scope;
6+
7+
use crate::env::{EnvLen, Index, Level};
68
use crate::source::Span;
79
use crate::symbol::Symbol;
810

@@ -206,6 +208,10 @@ pub enum Term<'arena> {
206208
}
207209

208210
impl<'arena> Term<'arena> {
211+
pub fn error(span: impl Into<Span>) -> Self {
212+
Self::Prim(span.into(), Prim::ReportedError)
213+
}
214+
209215
/// Get the source span of the term.
210216
pub fn span(&self) -> Span {
211217
match self {
@@ -280,6 +286,155 @@ impl<'arena> Term<'arena> {
280286
pub fn is_error(&self) -> bool {
281287
matches!(self, Term::Prim(_, Prim::ReportedError))
282288
}
289+
290+
// TODO: Add a new `Weaken` variant to `core::Term` instead of eagerly
291+
// traversing the term? See [Andras Kovacs’ staged language](https://github.com/AndrasKovacs/staged/blob/9e381eb162f44912d70fb843c4ca6567b0d1683a/demo/Syntax.hs#L52) for an example
292+
pub fn shift(&self, scope: &'arena Scope<'arena>, amount: EnvLen) -> Term<'arena> {
293+
self.shift_inner(scope, Index::last(), amount)
294+
}
295+
296+
/// Increment all `LocalVar`s greater than or equal to `min` by `amount`
297+
fn shift_inner(
298+
&self,
299+
scope: &'arena Scope<'arena>,
300+
mut min: Index,
301+
amount: EnvLen,
302+
) -> Term<'arena> {
303+
// Skip traversing and rebuilding the term if it would make no change. Increases
304+
// sharing.
305+
if amount == EnvLen::new() {
306+
return self.clone();
307+
}
308+
309+
match self {
310+
Term::LocalVar(span, var) if *var >= min => Term::LocalVar(*span, *var + amount),
311+
Term::LocalVar(..)
312+
| Term::ItemVar(..)
313+
| Term::MetaVar(..)
314+
| Term::InsertedMeta(..)
315+
| Term::Prim(..)
316+
| Term::ConstLit(..)
317+
| Term::Universe(..) => self.clone(),
318+
Term::Ann(span, expr, r#type) => Term::Ann(
319+
*span,
320+
scope.to_scope(expr.shift_inner(scope, min, amount)),
321+
scope.to_scope(r#type.shift_inner(scope, min, amount)),
322+
),
323+
Term::Let(span, name, def_type, def_expr, body) => Term::Let(
324+
*span,
325+
*name,
326+
scope.to_scope(def_type.shift_inner(scope, min, amount)),
327+
scope.to_scope(def_expr.shift_inner(scope, min, amount)),
328+
scope.to_scope(body.shift_inner(scope, min.prev(), amount)),
329+
),
330+
Term::FunType(span, plicity, name, input, output) => Term::FunType(
331+
*span,
332+
*plicity,
333+
*name,
334+
scope.to_scope(input.shift_inner(scope, min, amount)),
335+
scope.to_scope(output.shift_inner(scope, min.prev(), amount)),
336+
),
337+
Term::FunLit(span, plicity, name, body) => Term::FunLit(
338+
*span,
339+
*plicity,
340+
*name,
341+
scope.to_scope(body.shift_inner(scope, min.prev(), amount)),
342+
),
343+
Term::FunApp(span, plicity, fun, arg) => Term::FunApp(
344+
*span,
345+
*plicity,
346+
scope.to_scope(fun.shift_inner(scope, min, amount)),
347+
scope.to_scope(arg.shift_inner(scope, min, amount)),
348+
),
349+
Term::RecordType(span, labels, types) => Term::RecordType(
350+
*span,
351+
labels,
352+
scope.to_scope_from_iter(types.iter().map(|r#type| {
353+
let ret = r#type.shift_inner(scope, min, amount);
354+
min = min.prev();
355+
ret
356+
})),
357+
),
358+
Term::RecordLit(span, labels, exprs) => Term::RecordLit(
359+
*span,
360+
labels,
361+
scope.to_scope_from_iter(
362+
exprs
363+
.iter()
364+
.map(|expr| expr.shift_inner(scope, min, amount)),
365+
),
366+
),
367+
Term::RecordProj(span, head, label) => Term::RecordProj(
368+
*span,
369+
scope.to_scope(head.shift_inner(scope, min, amount)),
370+
*label,
371+
),
372+
Term::ArrayLit(span, terms) => Term::ArrayLit(
373+
*span,
374+
scope.to_scope_from_iter(
375+
terms
376+
.iter()
377+
.map(|term| term.shift_inner(scope, min, amount)),
378+
),
379+
),
380+
Term::FormatRecord(span, labels, terms) => Term::FormatRecord(
381+
*span,
382+
labels,
383+
scope.to_scope_from_iter(terms.iter().map(|term| {
384+
let ret = term.shift_inner(scope, min, amount);
385+
min = min.prev();
386+
ret
387+
})),
388+
),
389+
Term::FormatCond(span, name, format, pred) => Term::FormatCond(
390+
*span,
391+
*name,
392+
scope.to_scope(format.shift_inner(scope, min, amount)),
393+
scope.to_scope(pred.shift_inner(scope, min.prev(), amount)),
394+
),
395+
Term::FormatOverlap(span, labels, terms) => Term::FormatOverlap(
396+
*span,
397+
labels,
398+
scope.to_scope_from_iter(terms.iter().map(|term| {
399+
let ret = term.shift_inner(scope, min, amount);
400+
min = min.prev();
401+
ret
402+
})),
403+
),
404+
Term::ConstMatch(span, scrut, branches, default) => Term::ConstMatch(
405+
*span,
406+
scope.to_scope(scrut.shift_inner(scope, min, amount)),
407+
scope.to_scope_from_iter(
408+
branches
409+
.iter()
410+
.map(|(r#const, term)| (*r#const, term.shift_inner(scope, min, amount))),
411+
),
412+
default.map(|(name, term)| {
413+
(
414+
name,
415+
scope.to_scope(term.shift_inner(scope, min.prev(), amount)) as &_,
416+
)
417+
}),
418+
),
419+
}
420+
}
421+
422+
/// Returns `true` if `self` can be evaluated in a single step.
423+
/// Used as a heuristic to prevent increase in runtime when expanding
424+
/// pattern matches
425+
pub fn is_atomic(&self) -> bool {
426+
match self {
427+
Term::ItemVar(_, _)
428+
| Term::LocalVar(_, _)
429+
| Term::MetaVar(_, _)
430+
| Term::InsertedMeta(_, _, _)
431+
| Term::Universe(_)
432+
| Term::Prim(_, _)
433+
| Term::ConstLit(_, _) => true,
434+
Term::RecordProj(_, head, _) => head.is_atomic(),
435+
_ => false,
436+
}
437+
}
283438
}
284439

285440
macro_rules! def_prims {
@@ -600,6 +755,21 @@ pub enum Const {
600755
Ref(usize),
601756
}
602757

758+
impl Const {
759+
/// Return the number of inhabitants of `self`.
760+
/// `None` represents infinity
761+
pub fn num_inhabitants(&self) -> Option<u128> {
762+
match self {
763+
Const::Bool(_) => Some(2),
764+
Const::U8(_, _) | Const::S8(_) => Some(1 << 8),
765+
Const::U16(_, _) | Const::S16(_) => Some(1 << 16),
766+
Const::U32(_, _) | Const::S32(_) => Some(1 << 32),
767+
Const::U64(_, _) | Const::S64(_) => Some(1 << 64),
768+
Const::F32(_) | Const::F64(_) | Const::Pos(_) | Const::Ref(_) => None,
769+
}
770+
}
771+
}
772+
603773
impl PartialEq for Const {
604774
fn eq(&self, other: &Const) -> bool {
605775
match (*self, *other) {

fathom/src/core/semantics.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ pub enum Value<'arena> {
5454
}
5555

5656
impl<'arena> Value<'arena> {
57+
pub const ERROR: Self = Self::Stuck(Head::Prim(Prim::ReportedError), Vec::new());
58+
5759
pub fn prim(prim: Prim, params: impl IntoIterator<Item = ArcValue<'arena>>) -> Value<'arena> {
5860
let params = params
5961
.into_iter()
@@ -77,6 +79,13 @@ impl<'arena> Value<'arena> {
7779
}
7880
}
7981

82+
pub fn match_record_type(&self) -> Option<&Telescope<'arena>> {
83+
match self {
84+
Value::RecordType(_, telescope) => Some(telescope),
85+
_ => None,
86+
}
87+
}
88+
8089
pub fn is_error(&self) -> bool {
8190
matches!(self, Value::Stuck(Head::Prim(Prim::ReportedError), _))
8291
}

fathom/src/env.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
//! [`SharedEnv`] to increase the amount of sharing at the expense of locality.
1919
2020
use std::fmt;
21+
use std::ops::Add;
2122

2223
/// Underlying variable representation.
2324
type RawVar = usize;
@@ -56,6 +57,13 @@ impl Index {
5657
}
5758
}
5859

60+
impl Add<EnvLen> for Index {
61+
type Output = Self;
62+
fn add(self, rhs: EnvLen) -> Self::Output {
63+
Self(self.0 + rhs.0) // FIXME: check overflow?
64+
}
65+
}
66+
5967
impl fmt::Debug for Index {
6068
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6169
write!(f, "Index(")?;
@@ -126,6 +134,13 @@ pub fn levels() -> impl Iterator<Item = Level> {
126134
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
127135
pub struct EnvLen(RawVar);
128136

137+
impl Add<Index> for EnvLen {
138+
type Output = Self;
139+
fn add(self, rhs: Index) -> Self::Output {
140+
Self(self.0 + rhs.0) // FIXME: check overflow?
141+
}
142+
}
143+
129144
impl EnvLen {
130145
/// Construct a new, empty environment.
131146
pub fn new() -> EnvLen {
@@ -152,6 +167,10 @@ impl EnvLen {
152167
Level(self.0)
153168
}
154169

170+
pub fn next(&self) -> EnvLen {
171+
Self(self.0 + 1) // FIXME: check overflow?
172+
}
173+
155174
/// Push an entry onto the environment.
156175
pub fn push(&mut self) {
157176
self.0 += 1;

fathom/src/surface.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub struct ItemDef<'arena, Range> {
7575

7676
/// Surface patterns.
7777
#[derive(Debug, Clone)]
78-
pub enum Pattern<Range> {
78+
pub enum Pattern<'arena, Range> {
7979
/// Named patterns, eg. `x`, `true`, `false`
8080
Name(Range, Symbol),
8181
/// Placeholder patterns, eg. `_`
@@ -92,8 +92,10 @@ pub enum Pattern<Range> {
9292
NumberLiteral(Range, Symbol),
9393
/// Boolean literal patterns
9494
BooleanLiteral(Range, bool),
95-
// TODO: Record literal patterns
96-
// RecordLiteral(Range, &'arena [((Range, StringId), Pattern<'arena, Range>)]),
95+
/// Record literal patterns
96+
RecordLiteral(Range, &'arena [PatternField<'arena, Range>]),
97+
/// Tuple literal patterns
98+
TupleLiteral(Range, &'arena [Pattern<'arena, Range>]),
9799
}
98100

99101
#[derive(Debug, Clone, Copy)]
@@ -166,14 +168,16 @@ impl<Range> fmt::Display for BinOp<Range> {
166168
}
167169
}
168170

169-
impl<Range: Clone> Pattern<Range> {
171+
impl<'arena, Range: Clone> Pattern<'arena, Range> {
170172
pub fn range(&self) -> Range {
171173
match self {
172174
Pattern::Name(range, _)
173175
| Pattern::Placeholder(range)
174176
| Pattern::StringLiteral(range, _)
175177
| Pattern::NumberLiteral(range, _)
176-
| Pattern::BooleanLiteral(range, _) => range.clone(),
178+
| Pattern::BooleanLiteral(range, _)
179+
| Pattern::RecordLiteral(range, _)
180+
| Pattern::TupleLiteral(range, _) => range.clone(),
177181
}
178182
}
179183
}
@@ -198,7 +202,7 @@ pub enum Term<'arena, Range> {
198202
/// Let expressions.
199203
Let(
200204
Range,
201-
Pattern<Range>,
205+
&'arena Pattern<'arena, Range>,
202206
Option<&'arena Term<'arena, Range>>,
203207
&'arena Term<'arena, Range>,
204208
&'arena Term<'arena, Range>,
@@ -214,7 +218,7 @@ pub enum Term<'arena, Range> {
214218
Match(
215219
Range,
216220
&'arena Term<'arena, Range>,
217-
&'arena [(Pattern<Range>, Term<'arena, Range>)],
221+
&'arena [(Pattern<'arena, Range>, Term<'arena, Range>)],
218222
),
219223
/// The type of types.
220224
Universe(Range),
@@ -350,7 +354,7 @@ impl<'arena> Term<'arena, FileRange> {
350354
#[derive(Debug, Clone)]
351355
pub struct Param<'arena, Range> {
352356
pub plicity: Plicity,
353-
pub pattern: Pattern<Range>,
357+
pub pattern: Pattern<'arena, Range>,
354358
pub r#type: Option<Term<'arena, Range>>,
355359
}
356360

@@ -402,6 +406,15 @@ pub struct ExprField<'arena, Range> {
402406
expr: Option<Term<'arena, Range>>,
403407
}
404408

409+
/// A field definition in a record pattern
410+
#[derive(Debug, Clone)]
411+
pub struct PatternField<'arena, Range> {
412+
/// Label identifying the field
413+
label: (Range, Symbol),
414+
/// The pattern that this field will match
415+
pattern: Pattern<'arena, Range>,
416+
}
417+
405418
/// Messages produced during parsing
406419
#[derive(Clone, Debug)]
407420
pub enum ParseMessage {
@@ -527,14 +540,14 @@ mod tests {
527540
#[test]
528541
#[cfg(target_pointer_width = "64")]
529542
fn term_size() {
530-
assert_eq!(std::mem::size_of::<Term<()>>(), 32);
543+
assert_eq!(std::mem::size_of::<Term<()>>(), 40);
531544
assert_eq!(std::mem::size_of::<Term<ByteRange>>(), 48);
532545
}
533546

534547
#[test]
535548
#[cfg(target_pointer_width = "64")]
536549
fn pattern_size() {
537-
assert_eq!(std::mem::size_of::<Pattern<()>>(), 8);
538-
assert_eq!(std::mem::size_of::<Pattern<ByteRange>>(), 16);
550+
assert_eq!(std::mem::size_of::<Pattern<()>>(), 24);
551+
assert_eq!(std::mem::size_of::<Pattern<ByteRange>>(), 32);
539552
}
540553
}

0 commit comments

Comments
 (0)