Skip to content

Commit e5f4772

Browse files
committed
Use new pattern match compiler and coverage checker
1 parent 02c4894 commit e5f4772

Some content is hidden

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

54 files changed

+1841
-651
lines changed

fathom/src/core.rs

Lines changed: 170 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
33
use std::fmt;
44

5-
use crate::env::{Index, Level};
6-
use crate::source::{FileRange, Span, StringId};
5+
use scoped_arena::Scope;
6+
7+
use crate::env::{EnvLen, Index, Level};
8+
use crate::source::{Span, StringId};
79

810
pub mod binary;
911
pub mod pretty;
@@ -205,6 +207,10 @@ pub enum Term<'arena> {
205207
}
206208

207209
impl<'arena> Term<'arena> {
210+
pub fn error(span: impl Into<Span>) -> Self {
211+
Self::Prim(span.into(), Prim::ReportedError)
212+
}
213+
208214
/// Get the source span of the term.
209215
pub fn span(&self) -> Span {
210216
match self {
@@ -280,8 +286,153 @@ impl<'arena> Term<'arena> {
280286
matches!(self, Term::Prim(_, Prim::ReportedError))
281287
}
282288

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

@@ -603,6 +754,21 @@ pub enum Const {
603754
Ref(usize),
604755
}
605756

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

fathom/src/core/semantics.rs

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

5555
impl<'arena> Value<'arena> {
56+
pub const ERROR: Self = Self::Stuck(Head::Prim(Prim::ReportedError), Vec::new());
57+
5658
pub fn prim(prim: Prim, params: impl IntoIterator<Item = ArcValue<'arena>>) -> Value<'arena> {
5759
let params = params
5860
.into_iter()
@@ -76,6 +78,13 @@ impl<'arena> Value<'arena> {
7678
}
7779
}
7880

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

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 = u16;
@@ -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; // FIXME: check overflow?

fathom/src/surface.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ pub enum Term<'arena, Range> {
203203
/// Let expressions.
204204
Let(
205205
Range,
206-
Pattern<'arena, Range>,
206+
&'arena Pattern<'arena, Range>,
207207
Option<&'arena Term<'arena, Range>>,
208208
&'arena Term<'arena, Range>,
209209
&'arena Term<'arena, Range>,
@@ -542,9 +542,9 @@ mod tests {
542542
#[test]
543543
#[cfg(target_pointer_width = "64")]
544544
fn term_size() {
545-
assert_eq!(std::mem::size_of::<Term<()>>(), 48);
546-
assert_eq!(std::mem::size_of::<Term<ByteRange>>(), 64);
547-
assert_eq!(std::mem::size_of::<Term<FileRange>>(), 72);
545+
assert_eq!(std::mem::size_of::<Term<()>>(), 40);
546+
assert_eq!(std::mem::size_of::<Term<ByteRange>>(), 48);
547+
assert_eq!(std::mem::size_of::<Term<FileRange>>(), 48);
548548
}
549549

550550
#[test]

0 commit comments

Comments
 (0)