mirror of
https://github.com/oxalica/nil.git
synced 2025-12-23 09:19:49 +00:00
Tweak type formatter
This commit is contained in:
parent
e8659c2e08
commit
f43380d009
3 changed files with 100 additions and 37 deletions
|
|
@ -1,20 +1,24 @@
|
|||
//! TODO: Limit type length.
|
||||
use super::{InferenceResult, Ty, TyKind};
|
||||
use std::fmt;
|
||||
|
||||
const MAX_FIELD_CNT: usize = 8;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TyDisplay<'a> {
|
||||
ty: Ty,
|
||||
infer: &'a InferenceResult,
|
||||
depth: usize,
|
||||
in_param: bool,
|
||||
}
|
||||
|
||||
impl<'a> TyDisplay<'a> {
|
||||
pub fn new(ty: Ty, infer: &'a InferenceResult) -> Self {
|
||||
Self { ty, infer }
|
||||
}
|
||||
|
||||
fn with(self, ty: Ty) -> Self {
|
||||
Self::new(ty, self.infer)
|
||||
pub fn new(ty: Ty, infer: &'a InferenceResult, depth: usize) -> Self {
|
||||
Self {
|
||||
ty,
|
||||
infer,
|
||||
depth,
|
||||
in_param: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -27,21 +31,74 @@ impl fmt::Display for TyDisplay<'_> {
|
|||
TyKind::Float => "float".fmt(f),
|
||||
TyKind::String => "string".fmt(f),
|
||||
TyKind::Path => "path".fmt(f),
|
||||
&TyKind::List(elem) => write!(f, "[{}]", self.with(elem)),
|
||||
&TyKind::Lambda(a, b) => write!(f, "{} -> {}", self.with(a), self.with(b)),
|
||||
TyKind::Attrset(set) => {
|
||||
write!(f, "{{")?;
|
||||
let mut first = true;
|
||||
for (name, ty) in set.iter() {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(f, ",")?;
|
||||
}
|
||||
// FIXME: Escape field names.
|
||||
write!(f, " {}: {}", name, self.with(ty))?;
|
||||
&TyKind::List(ty) => {
|
||||
if self.depth == 0 {
|
||||
"[…]".fmt(f)
|
||||
} else {
|
||||
let elem = Self {
|
||||
ty,
|
||||
infer: self.infer,
|
||||
depth: self.depth - 1,
|
||||
in_param: false,
|
||||
};
|
||||
write!(f, "[{}]", elem)
|
||||
}
|
||||
}
|
||||
&TyKind::Lambda(param, ret) => {
|
||||
if self.in_param {
|
||||
"(".fmt(f)?;
|
||||
}
|
||||
if self.depth == 0 {
|
||||
"… → …".fmt(f)?;
|
||||
} else {
|
||||
let param = Self {
|
||||
ty: param,
|
||||
infer: self.infer,
|
||||
// Show full lambda type.
|
||||
depth: self.depth,
|
||||
in_param: true,
|
||||
};
|
||||
let ret = Self {
|
||||
ty: ret,
|
||||
infer: self.infer,
|
||||
// Show full lambda type.
|
||||
depth: self.depth,
|
||||
in_param: false,
|
||||
};
|
||||
write!(f, "{} → {}", param, ret)?;
|
||||
}
|
||||
if self.in_param {
|
||||
")".fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
TyKind::Attrset(set) => {
|
||||
if self.depth == 0 {
|
||||
"{ … }".fmt(f)
|
||||
} else {
|
||||
"{".fmt(f)?;
|
||||
let mut first = true;
|
||||
for (name, ty) in set.iter().take(MAX_FIELD_CNT) {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
",".fmt(f)?;
|
||||
}
|
||||
// FIXME: Escape field names.
|
||||
let value = Self {
|
||||
ty,
|
||||
infer: self.infer,
|
||||
depth: self.depth - 1,
|
||||
in_param: false,
|
||||
};
|
||||
write!(f, " {}: {}", name, value)?;
|
||||
}
|
||||
if set.len() > MAX_FIELD_CNT {
|
||||
", … }".fmt(f)
|
||||
} else {
|
||||
" }".fmt(f)
|
||||
}
|
||||
}
|
||||
write!(f, " }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,14 @@ impl TyKind {
|
|||
pub struct Attrset(BTreeMap<SmolStr, Ty>);
|
||||
|
||||
impl Attrset {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn get(&self, field: &str) -> Option<Ty> {
|
||||
self.0.get(field).copied()
|
||||
}
|
||||
|
|
@ -85,7 +93,11 @@ impl InferenceResult {
|
|||
}
|
||||
|
||||
pub fn display_ty(&self, ty: Ty) -> TyDisplay<'_> {
|
||||
TyDisplay::new(ty, self)
|
||||
TyDisplay::new(ty, self, 2)
|
||||
}
|
||||
|
||||
pub fn debug_ty(&self, ty: Ty) -> TyDisplay<'_> {
|
||||
TyDisplay::new(ty, self, usize::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ fn check(src: &str, expect: Expect) {
|
|||
let module = db.module(file);
|
||||
let infer = db.infer(file);
|
||||
let ty = infer.ty_for_expr(module.entry_expr());
|
||||
let got = infer.display_ty(ty).to_string();
|
||||
let got = infer.debug_ty(ty).to_string();
|
||||
expect.assert_eq(&got);
|
||||
}
|
||||
|
||||
|
|
@ -17,16 +17,10 @@ fn check_all(src: &str, expect: Expect) {
|
|||
let infer = db.infer(file);
|
||||
let got = module
|
||||
.names()
|
||||
.map(|(i, name)| {
|
||||
format!(
|
||||
"{}: {}\n",
|
||||
name.text,
|
||||
infer.display_ty(infer.ty_for_name(i))
|
||||
)
|
||||
})
|
||||
.map(|(i, name)| format!("{}: {}\n", name.text, infer.debug_ty(infer.ty_for_name(i))))
|
||||
.chain([format!(
|
||||
": {}\n",
|
||||
infer.display_ty(infer.ty_for_expr(module.entry_expr()))
|
||||
infer.debug_ty(infer.ty_for_expr(module.entry_expr()))
|
||||
)])
|
||||
.collect::<String>();
|
||||
expect.assert_eq(&got);
|
||||
|
|
@ -116,32 +110,32 @@ fn if_then_else() {
|
|||
|
||||
#[test]
|
||||
fn lambda() {
|
||||
check("a: a", expect!["? -> ?"]);
|
||||
check("a: a", expect!["? → ?"]);
|
||||
check("(a: a) 1", expect!["int"]);
|
||||
|
||||
check("{ }: 1", expect!["{ } -> int"]);
|
||||
check("{ }: 1", expect!["{ } → int"]);
|
||||
check_all(
|
||||
"{ a ? b, b ? 42 }@c: b",
|
||||
expect![[r#"
|
||||
c: { a: int, b: int }
|
||||
a: int
|
||||
b: int
|
||||
: { a: int, b: int } -> int
|
||||
: { a: int, b: int } → int
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select() {
|
||||
check("a: a.b.c", expect!["{ b: { c: ? } } -> ?"]);
|
||||
check("a: a.b.c or 42", expect!["{ b: { c: int } } -> int"]);
|
||||
check("a: a.b.c", expect!["{ b: { c: ? } } → ?"]);
|
||||
check("a: a.b.c or 42", expect!["{ b: { c: int } } → int"]);
|
||||
|
||||
check_all(
|
||||
"a: b: a.${b}",
|
||||
expect![[r#"
|
||||
a: { }
|
||||
b: string
|
||||
: { } -> string -> ?
|
||||
: { } → string → ?
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue