mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 13:13:43 +00:00
feat: elementary tuple method checker (#669)
* feat: elementary tuple method checker * dev: remove println!
This commit is contained in:
parent
191c943936
commit
5898c60de1
10 changed files with 262 additions and 31 deletions
|
@ -12,7 +12,7 @@ use crate::adt::interner::Interned;
|
|||
pub struct ApplyTypeChecker<'a, 'b, 'w> {
|
||||
pub(super) base: &'a mut TypeChecker<'b, 'w>,
|
||||
pub call_site: Span,
|
||||
pub args: ast::Args<'a>,
|
||||
pub args: Option<ast::Args<'a>>,
|
||||
pub resultant: Vec<Ty>,
|
||||
}
|
||||
|
||||
|
@ -44,23 +44,114 @@ impl<'a, 'b, 'w> ApplyChecker for ApplyTypeChecker<'a, 'b, 'w> {
|
|||
}
|
||||
|
||||
// todo: remove this after we implemented dependent types
|
||||
if let Sig::TypeCons { val, .. } = sig {
|
||||
if *val == typst::foundations::Type::of::<typst::foundations::Type>() {
|
||||
if let Some(p0) = args.pos(0) {
|
||||
self.resultant
|
||||
.push(Ty::Unary(TypeUnary::new(UnaryOp::TypeOf, p0.into())));
|
||||
match sig {
|
||||
Sig::TypeCons { val, .. } => {
|
||||
if *val == typst::foundations::Type::of::<typst::foundations::Type>() {
|
||||
if let Some(p0) = args.pos(0) {
|
||||
self.resultant
|
||||
.push(Ty::Unary(TypeUnary::new(UnaryOp::TypeOf, p0.into())));
|
||||
}
|
||||
}
|
||||
}
|
||||
Sig::Builtin(BuiltinSig::TupleMap(this)) => {
|
||||
if let Some(p0) = args.pos(0) {
|
||||
let mut resultants = vec![];
|
||||
log::debug!("syntax check tuple map {this:?} {p0:?}");
|
||||
let mut mapper = |base: &mut TypeChecker, sig: Sig<'_>, _pol| {
|
||||
resultants.push(Ty::Any);
|
||||
|
||||
match sig {
|
||||
Sig::TupleCons(cons) => {
|
||||
let res = cons
|
||||
.iter()
|
||||
.map(|elem| {
|
||||
log::debug!(
|
||||
"tuple map check on tuple elem: {elem:?} {p0:?}"
|
||||
);
|
||||
let args = ArgsTy::unary(elem.clone(), Ty::Any);
|
||||
let mut mapper = ApplyTypeChecker {
|
||||
base,
|
||||
call_site: Span::detached(),
|
||||
args: None,
|
||||
resultant: vec![],
|
||||
};
|
||||
p0.call(&args, true, &mut mapper);
|
||||
Ty::from_types(mapper.resultant.into_iter())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.resultant.push(Ty::Tuple(res.into()));
|
||||
}
|
||||
Sig::ArrayCons(elem) => {
|
||||
log::debug!("array map check on array: {elem:?} {p0:?}");
|
||||
let args = ArgsTy::unary(elem.as_ref().clone(), Ty::Any);
|
||||
let mut mapper = ApplyTypeChecker {
|
||||
base,
|
||||
call_site: Span::detached(),
|
||||
args: None,
|
||||
resultant: vec![],
|
||||
};
|
||||
p0.call(&args, true, &mut mapper);
|
||||
let res = Ty::from_types(mapper.resultant.into_iter());
|
||||
self.resultant.push(Ty::Array(res.into()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
let mut worker = TupleChecker {
|
||||
base: self.base,
|
||||
driver: &mut mapper,
|
||||
};
|
||||
this.tuple_element_of(pol, &mut worker);
|
||||
log::debug!("resultant: {resultants:?}");
|
||||
}
|
||||
}
|
||||
Sig::Builtin(BuiltinSig::TupleAt(this)) => {
|
||||
if let Some(p0) = args.pos(0) {
|
||||
let mut resultants = vec![];
|
||||
log::debug!("syntax check tuple at {this:?} {p0:?}");
|
||||
|
||||
// todo: caster
|
||||
let selector = match p0 {
|
||||
Ty::Value(v) => match v.val {
|
||||
Value::Int(i) => Ok(i as usize),
|
||||
Value::Float(i) => Ok(i as usize),
|
||||
_ => Err(p0),
|
||||
},
|
||||
ty => Err(ty),
|
||||
};
|
||||
|
||||
let mut mapper = |_base: &mut TypeChecker, sig: Sig<'_>, _pol| {
|
||||
resultants.push(Ty::Any);
|
||||
|
||||
match sig {
|
||||
Sig::TupleCons(cons) => {
|
||||
log::debug!("tuple at check on tuple elem: {cons:?} {p0:?}");
|
||||
let sel = match selector {
|
||||
Ok(i) => cons.get(i).cloned(),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let res =
|
||||
sel.unwrap_or_else(|| Ty::from_types(cons.iter().cloned()));
|
||||
self.resultant.push(res);
|
||||
}
|
||||
Sig::ArrayCons(elem) => {
|
||||
log::debug!("array at check on array: {elem:?} {p0:?}");
|
||||
self.resultant.push(elem.as_ref().clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
let mut worker = TupleChecker {
|
||||
base: self.base,
|
||||
driver: &mut mapper,
|
||||
};
|
||||
this.tuple_element_of(pol, &mut worker);
|
||||
log::debug!("resultant: {resultants:?}");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// let v = val.inner();
|
||||
// use typst::foundations::func::Repr;
|
||||
// if let Repr::Native(v) = v {
|
||||
// match v.name {
|
||||
// "assert" => {}
|
||||
// "panic" => {}
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
|
||||
let callee = sig.ty();
|
||||
|
||||
|
@ -108,3 +199,36 @@ impl<'a, 'b, 'w> ApplyChecker for ApplyTypeChecker<'a, 'b, 'w> {
|
|||
// );
|
||||
}
|
||||
}
|
||||
|
||||
trait TupleCheckDriver {
|
||||
fn check(&mut self, base: &mut TypeChecker, sig: Sig, pol: bool);
|
||||
}
|
||||
|
||||
impl<T: FnMut(&mut TypeChecker, Sig, bool)> TupleCheckDriver for T {
|
||||
fn check(&mut self, base: &mut TypeChecker, sig: Sig, pol: bool) {
|
||||
self(base, sig, pol);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TupleChecker<'a, 'b, 'w> {
|
||||
pub(super) base: &'a mut TypeChecker<'b, 'w>,
|
||||
driver: &'a mut dyn TupleCheckDriver,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'w> ApplyChecker for TupleChecker<'a, 'b, 'w> {
|
||||
fn bound_of_var(
|
||||
&mut self,
|
||||
var: &Interned<super::TypeVar>,
|
||||
_pol: bool,
|
||||
) -> Option<super::TypeBounds> {
|
||||
self.base
|
||||
.info
|
||||
.vars
|
||||
.get(&var.def)
|
||||
.map(|v| v.bounds.bounds().read().clone())
|
||||
}
|
||||
|
||||
fn apply(&mut self, sig: Sig, _args: &Interned<ArgsTy>, pol: bool) {
|
||||
self.driver.check(self.base, sig, pol);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -333,7 +333,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
let mut worker = ApplyTypeChecker {
|
||||
base: self,
|
||||
call_site: func_call.callee().span(),
|
||||
args: func_call.args(),
|
||||
args: Some(func_call.args()),
|
||||
resultant: vec![],
|
||||
};
|
||||
callee.call(&args, true, &mut worker);
|
||||
|
@ -467,7 +467,7 @@ impl<'a, 'w> TypeChecker<'a, 'w> {
|
|||
let mut worker = ApplyTypeChecker {
|
||||
base: self,
|
||||
call_site: set_rule.target().span(),
|
||||
args: set_rule.args(),
|
||||
args: Some(set_rule.args()),
|
||||
resultant: vec![],
|
||||
};
|
||||
callee.call(&args, true, &mut worker);
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/type_check/tuple_at.typ
|
||||
---
|
||||
"a" = (1, 2, 3, )
|
||||
"b" = 2
|
||||
---
|
||||
5..6 -> @a
|
||||
25..26 -> @b
|
||||
29..30 -> @a
|
||||
29..36 -> 2
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/analysis.rs
|
||||
expression: result
|
||||
input_file: crates/tinymist-query/src/fixtures/type_check/tuple_map.typ
|
||||
---
|
||||
"a" = (1, )
|
||||
"b" = (Type(string), )
|
||||
"f" = (( ⪯ (Type(bytes) | Type(float) | Type(integer) | Type(label) | Type(string) | Type(type) | Type(version)))) => Type(string)
|
||||
"x" = Any
|
||||
---
|
||||
5..6 -> @a
|
||||
20..21 -> @f
|
||||
24..25 -> @x
|
||||
29..32 -> Type(string)
|
||||
29..35 -> Type(string)
|
||||
33..34 -> @x
|
||||
42..43 -> @b
|
||||
46..47 -> @a
|
||||
46..54 -> (Type(string), )
|
||||
52..53 -> @f
|
|
@ -0,0 +1,2 @@
|
|||
#let a = (1, 2, 3);
|
||||
#let b = a.at(1);
|
|
@ -0,0 +1,3 @@
|
|||
#let a = (1,);
|
||||
#let f = x => str(x);
|
||||
#let b = a.map(f);
|
|
@ -12,16 +12,21 @@ pub trait ApplyChecker {
|
|||
}
|
||||
}
|
||||
|
||||
static EMPTY_ARGS: Lazy<Interned<ArgsTy>> = Lazy::new(|| ArgsTy::default().into());
|
||||
|
||||
impl Ty {
|
||||
/// Call the given type with the given arguments.
|
||||
pub fn call(&self, args: &Interned<ArgsTy>, pol: bool, checker: &mut impl ApplyChecker) {
|
||||
self.apply(SigSurfaceKind::Call, args, pol, checker)
|
||||
}
|
||||
|
||||
/// Get the tuple element type of the given type.
|
||||
pub fn tuple_element_of(&self, pol: bool, checker: &mut impl ApplyChecker) {
|
||||
self.apply(SigSurfaceKind::Array, &EMPTY_ARGS, pol, checker)
|
||||
}
|
||||
|
||||
/// Get the element type of the given type.
|
||||
pub fn element_of(&self, pol: bool, checker: &mut impl ApplyChecker) {
|
||||
static EMPTY_ARGS: Lazy<Interned<ArgsTy>> = Lazy::new(|| ArgsTy::default().into());
|
||||
|
||||
self.apply(SigSurfaceKind::ArrayOrDict, &EMPTY_ARGS, pol, checker)
|
||||
}
|
||||
|
||||
|
|
|
@ -156,6 +156,15 @@ impl<'a> Iterator for UnionIter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// todo: we can write some proto files for builtin sigs
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BuiltinSig<'a> {
|
||||
/// Map a function over a tuple.
|
||||
TupleMap(&'a Ty),
|
||||
/// Get element of a tuple.
|
||||
TupleAt(&'a Ty),
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum BuiltinTy {
|
||||
Clause,
|
||||
|
|
|
@ -558,6 +558,37 @@ impl SigTy {
|
|||
})
|
||||
}
|
||||
|
||||
/// Unary constructor
|
||||
#[comemo::memoize]
|
||||
pub fn unary(inp: Ty, ret: Ty) -> Interned<SigTy> {
|
||||
Interned::new(Self {
|
||||
inputs: Interned::new(vec![inp]),
|
||||
body: Some(ret),
|
||||
names: NameBone::empty(),
|
||||
name_started: 0,
|
||||
spread_left: false,
|
||||
spread_right: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Tuple constructor
|
||||
#[comemo::memoize]
|
||||
pub fn tuple_cons(elems: Interned<Vec<Ty>>, anyify: bool) -> Interned<SigTy> {
|
||||
let ret = if anyify {
|
||||
Ty::Any
|
||||
} else {
|
||||
Ty::Tuple(elems.clone())
|
||||
};
|
||||
Interned::new(Self {
|
||||
inputs: elems,
|
||||
body: Some(ret),
|
||||
names: NameBone::empty(),
|
||||
name_started: 0,
|
||||
spread_left: false,
|
||||
spread_right: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Dictionary constructor
|
||||
#[comemo::memoize]
|
||||
pub fn dict_cons(named: &Interned<RecordTy>, anyify: bool) -> Interned<SigTy> {
|
||||
|
|
|
@ -4,12 +4,14 @@ use crate::{adt::interner::Interned, analysis::*, ty::def::*};
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Sig<'a> {
|
||||
Builtin(BuiltinSig<'a>),
|
||||
Type(&'a Interned<SigTy>),
|
||||
TypeCons {
|
||||
val: &'a typst::foundations::Type,
|
||||
at: &'a Ty,
|
||||
},
|
||||
ArrayCons(&'a TyRef),
|
||||
TupleCons(&'a Interned<Vec<Ty>>),
|
||||
DictCons(&'a Interned<RecordTy>),
|
||||
Value {
|
||||
val: &'a Func,
|
||||
|
@ -31,8 +33,10 @@ pub struct SigShape<'a> {
|
|||
impl<'a> Sig<'a> {
|
||||
pub fn ty(self) -> Option<Ty> {
|
||||
Some(match self {
|
||||
Sig::Builtin(_) => return None,
|
||||
Sig::Type(t) => Ty::Func(t.clone()),
|
||||
Sig::ArrayCons(t) => Ty::Array(t.clone()),
|
||||
Sig::TupleCons(t) => Ty::Tuple(t.clone()),
|
||||
Sig::DictCons(t) => Ty::Dict(t.clone()),
|
||||
Sig::TypeCons { val, .. } => Ty::Builtin(BuiltinTy::Type(*val)),
|
||||
Sig::Value { at, .. } => at.clone(),
|
||||
|
@ -48,7 +52,9 @@ impl<'a> Sig<'a> {
|
|||
};
|
||||
|
||||
let sig_ins = match cano_sig {
|
||||
Sig::Builtin(_) => return None,
|
||||
Sig::ArrayCons(a) => SigTy::array_cons(a.as_ref().clone(), false),
|
||||
Sig::TupleCons(t) => SigTy::tuple_cons(t.clone(), false),
|
||||
Sig::DictCons(d) => SigTy::dict_cons(d, false),
|
||||
Sig::TypeCons { val, .. } => ctx?.type_of_func(&val.constructor().ok()?)?,
|
||||
Sig::Value { val, .. } => ctx?.type_of_func(val)?,
|
||||
|
@ -160,6 +166,7 @@ impl<'a> SigCheckDriver<'a> {
|
|||
}
|
||||
|
||||
fn ty(&mut self, ty: &Ty, pol: bool) {
|
||||
log::debug!("check sig: {ty:?}");
|
||||
match ty {
|
||||
Ty::Builtin(BuiltinTy::Stroke) if self.dict_as_sig() => {
|
||||
self.checker
|
||||
|
@ -215,11 +222,11 @@ impl<'a> SigCheckDriver<'a> {
|
|||
self.checker.check(Sig::Type(sig), &mut self.ctx, pol);
|
||||
}
|
||||
Ty::Array(sig) if self.array_as_sig() => {
|
||||
// let sig = FlowSignature::array_cons(*sig.clone(), true);
|
||||
self.checker.check(Sig::ArrayCons(sig), &mut self.ctx, pol);
|
||||
}
|
||||
// todo: tuple
|
||||
Ty::Tuple(_) => {}
|
||||
Ty::Tuple(tup) if self.array_as_sig() => {
|
||||
self.checker.check(Sig::TupleCons(tup), &mut self.ctx, pol);
|
||||
}
|
||||
Ty::Dict(sig) if self.dict_as_sig() => {
|
||||
// self.check_dict_signature(sig, pol, self.checker);
|
||||
self.checker.check(Sig::DictCons(sig), &mut self.ctx, pol);
|
||||
|
@ -242,6 +249,7 @@ impl<'a> SigCheckDriver<'a> {
|
|||
|
||||
impl BoundChecker for SigCheckDriver<'_> {
|
||||
fn collect(&mut self, ty: &Ty, pol: bool) {
|
||||
log::debug!("sig bounds: {ty:?}");
|
||||
self.ty(ty, pol);
|
||||
}
|
||||
|
||||
|
@ -256,6 +264,17 @@ impl<'a, 'b> MethodDriver<'a, 'b> {
|
|||
fn is_binder(&self) -> bool {
|
||||
matches!(self.1.as_ref(), "with" | "where")
|
||||
}
|
||||
|
||||
fn array_method(&mut self, ty: &Ty, pol: bool) {
|
||||
let method = match self.1.as_ref() {
|
||||
"map" => BuiltinSig::TupleMap(ty),
|
||||
"at" => BuiltinSig::TupleAt(ty),
|
||||
_ => return,
|
||||
};
|
||||
self.0
|
||||
.checker
|
||||
.check(Sig::Builtin(method), &mut self.0.ctx, pol);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> BoundChecker for MethodDriver<'a, 'b> {
|
||||
|
@ -264,16 +283,20 @@ impl<'a, 'b> BoundChecker for MethodDriver<'a, 'b> {
|
|||
match ty {
|
||||
// todo: deduplicate checking early
|
||||
Ty::Value(v) => {
|
||||
if let Value::Func(f) = &v.val {
|
||||
if self.is_binder() {
|
||||
self.0.checker.check(
|
||||
Sig::Partialize(&Sig::Value { val: f, at: ty }),
|
||||
&mut self.0.ctx,
|
||||
pol,
|
||||
);
|
||||
} else {
|
||||
// todo: general select operator
|
||||
match &v.val {
|
||||
Value::Func(f) => {
|
||||
if self.is_binder() {
|
||||
self.0.checker.check(
|
||||
Sig::Partialize(&Sig::Value { val: f, at: ty }),
|
||||
&mut self.0.ctx,
|
||||
pol,
|
||||
);
|
||||
} else {
|
||||
// todo: general select operator
|
||||
}
|
||||
}
|
||||
Value::Array(..) => self.array_method(ty, pol),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ty::Builtin(BuiltinTy::Element(e)) => {
|
||||
|
@ -298,6 +321,8 @@ impl<'a, 'b> BoundChecker for MethodDriver<'a, 'b> {
|
|||
// todo: general select operator
|
||||
}
|
||||
}
|
||||
Ty::Tuple(..) => self.array_method(ty, pol),
|
||||
Ty::Array(..) => self.array_method(ty, pol),
|
||||
// todo: general select operator
|
||||
_ => {}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue