1use crate::core_syntax::{BindingContext, Term};
10use crate::frontend::WithSpan;
11use crate::utilities::Symbol;
12use std::collections::HashSet;
13use std::fmt::Debug;
14use std::vec;
15
16use crate::type_system::context::TypeContext;
17use pest::Span;
18use thiserror::Error;
19
20use self::binding_proxy::BindingProxy;
21
22pub mod binding_proxy;
23pub mod context;
24
25#[derive(Error, Debug)]
26pub enum TypeError<'a> {
27 #[error("sequential uniqueness requirement volidation")]
28 SequentialUniquenessViolation {
29 lhs: (Span<'a>, Type<'a>),
30 rhs: (Span<'a>, Type<'a>),
31 total: Span<'a>,
32 },
33 #[error("disjunctive uniqueness requirement volidation")]
34 DisjunctiveUniquenessViolation {
35 lhs: (Span<'a>, Type<'a>),
36 rhs: (Span<'a>, Type<'a>),
37 total: Span<'a>,
38 },
39 #[error("unguarded fixpoint {0}")]
40 UnguardedFixpoint(Symbol<'a>, Span<'a>),
41 #[error("unresolved reference {0}")]
42 UnresolvedReference(Symbol<'a>, Span<'a>),
43}
44
45#[derive(PartialEq, Eq, Debug, Clone)]
46pub struct Type<'src> {
47 pub first: HashSet<Symbol<'src>>,
48 pub follow: HashSet<Symbol<'src>>,
49 pub nullable: bool,
50 pub guarded: bool,
51}
52
53impl<'src> Type<'src> {
54 fn sequential_uniqueness(&self, other: &Self) -> bool {
55 !self.nullable && self.follow.is_disjoint(&other.first)
56 }
57 fn disjunctive_uniqueness(&self, other: &Self) -> bool {
58 !(self.nullable && other.nullable) && self.first.is_disjoint(&other.first)
59 }
60 fn epsilon() -> Self {
61 Self {
62 first: HashSet::new(),
63 follow: HashSet::new(),
64 nullable: true,
65 guarded: true,
66 }
67 }
68 fn token(token: Symbol<'src>) -> Self {
69 Self {
70 first: HashSet::from([token]),
71 follow: HashSet::new(),
72 nullable: false,
73 guarded: true,
74 }
75 }
76
77 fn sequence(
78 t1: &Self,
79 t2: &Self,
80 lhs: Span<'src>,
81 rhs: Span<'src>,
82 total: Span<'src>,
83 ) -> Result<Self, Box<TypeError<'src>>> {
84 if t1.sequential_uniqueness(t2) {
85 Ok(Self {
86 first: t1.first.clone(),
87 follow: if t2.nullable {
88 t2.follow
89 .union(&t2.first)
90 .chain(t1.follow.iter())
91 .cloned()
92 .collect()
93 } else {
94 t2.follow.clone()
95 },
96 nullable: false,
97 guarded: t1.guarded,
98 })
99 } else {
100 Err(Box::new(TypeError::SequentialUniquenessViolation {
102 lhs: (lhs, t1.clone()),
103 rhs: (rhs, t2.clone()),
104 total,
105 }))
106 }
107 }
108 fn bottom() -> Self {
109 Self {
110 first: HashSet::new(),
111 follow: HashSet::new(),
112 nullable: false,
113 guarded: true,
114 }
115 }
116
117 fn alternative(
118 t1: &Self,
119 t2: &Self,
120 lhs: Span<'src>,
121 rhs: Span<'src>,
122 total: Span<'src>,
123 ) -> Result<Self, Box<TypeError<'src>>> {
124 if t1.disjunctive_uniqueness(t2) {
125 Ok(Self {
126 first: t1.first.union(&t2.first).cloned().collect(),
127 follow: t1.follow.union(&t2.follow).cloned().collect(),
128 nullable: t1.nullable || t2.nullable,
129 guarded: t1.guarded && t2.guarded,
130 })
131 } else {
132 Err(Box::new(TypeError::DisjunctiveUniquenessViolation {
134 lhs: (lhs, t1.clone()),
135 rhs: (rhs, t2.clone()),
136 total,
137 }))
138 }
139 }
140 fn minimum() -> Self {
141 Self {
142 first: HashSet::new(),
143 follow: HashSet::new(),
144 nullable: false,
145 guarded: false,
146 }
147 }
148 fn fixpoint<F>(mut f: F) -> (Self, Vec<TypeError<'src>>)
149 where
150 F: FnMut(&Self) -> (Self, Vec<TypeError<'src>>),
151 {
152 let mut last = Self::minimum();
153 loop {
154 let (next, errs) = f(&last);
155 if !errs.is_empty() || next == last {
156 return (next, errs);
157 }
158 last = next;
159 }
160 }
161}
162
163fn type_check_impl<'src, 'a>(
164 typing_ctx: &mut TypeContext<'src>,
165 binding_ctx: &mut BindingProxy<'src, 'a>,
166 term: &'a WithSpan<'src, Term<'src, 'a>>,
167) -> (Type<'src>, Vec<TypeError<'src>>) {
168 match &term.node {
169 Term::Epsilon => (Type::epsilon(), vec![]),
170 Term::Sequence(x, y) => {
171 let (x_type, x_errors) = type_check_impl(typing_ctx, binding_ctx, x);
172 let (y_type, y_errors) = typing_ctx.guarded(|ctx| type_check_impl(ctx, binding_ctx, y));
173 let (r#type, err) = match Type::sequence(&x_type, &y_type, x.span, y.span, term.span) {
174 Ok(r#type) => (r#type, None),
175 Err(err) => (Type::bottom(), Some(err)),
176 };
177 (
178 r#type,
179 x_errors
180 .into_iter()
181 .chain(y_errors)
182 .chain(err.map(|e| *e))
183 .collect(),
184 )
185 }
186 Term::LexerRef(name) => (Type::token(*name), vec![]),
187 Term::Bottom => (Type::bottom(), vec![]),
188 Term::Alternative(x, y) => {
189 let (x_type, x_errors) = type_check_impl(typing_ctx, binding_ctx, x);
190 let (y_type, y_errors) = type_check_impl(typing_ctx, binding_ctx, y);
191 let (r#type, err) = match Type::alternative(&x_type, &y_type, x.span, y.span, term.span)
192 {
193 Ok(r#type) => (r#type, None),
194 Err(err) => (Type::bottom(), Some(err)),
195 };
196 (
197 r#type,
198 x_errors
199 .into_iter()
200 .chain(y_errors)
201 .chain(err.map(|e| *e))
202 .collect(),
203 )
204 }
205 Term::ParserRef(name) => {
206 if let Some(ty) = typing_ctx.lookup(*name) {
209 return (ty.as_ref().clone(), vec![]);
210 }
211 if let Some(target) = binding_ctx.lookup(name) {
213 let (r#type, errors) = binding_ctx.with_hiding(*name, |binding_ctx| {
215 type_check_impl(typing_ctx, binding_ctx, target)
216 });
217 (r#type, errors)
218 } else {
219 (
220 Type::bottom(),
221 vec![TypeError::UnresolvedReference(*name, term.span)],
222 )
223 }
224 }
225 Term::Fix(var, body) => {
226 if let Some(ty) = typing_ctx.lookup(*var) {
227 return (ty.as_ref().clone(), vec![]);
228 }
229 let (r#type, errs) = Type::fixpoint(|x| {
230 typing_ctx.with(*var, x.clone(), |ctx| {
231 type_check_impl(ctx, binding_ctx, body)
232 })
233 });
234 if !errs.is_empty() {
235 return (r#type, errs);
236 }
237 if r#type.guarded {
238 typing_ctx.with(*var, r#type.clone(), |ctx| {
239 type_check_impl(ctx, binding_ctx, body)
240 })
241 } else {
242 (
243 Type::bottom(),
244 vec![TypeError::UnguardedFixpoint(*var, term.span)],
245 )
246 }
247 }
248 }
249}
250
251pub fn type_check<'src, 'a>(
252 binding_ctx: &BindingContext<'src, 'a>,
253 term: &'a WithSpan<'src, Term<'src, 'a>>,
254 name: Symbol<'src>,
255) -> Vec<TypeError<'src>> {
256 let mut typing_ctx = TypeContext::new();
257 let mut proxy = BindingProxy::proxy(binding_ctx);
258 proxy.with_hiding(name, |binding_ctx| {
259 type_check_impl(&mut typing_ctx, binding_ctx, term).1
260 })
261}