use super::context::CheckerContext;
use tsz_parser::parser::NodeIndex;
use tsz_parser::parser::syntax_kind_ext;
use tsz_scanner::SyntaxKind;
use tsz_solver::TypeId;
use tsz_solver::recursion::{DepthCounter, RecursionProfile};
pub struct ExpressionChecker<'a, 'ctx> {
ctx: &'a mut CheckerContext<'ctx>,
depth: DepthCounter,
}
impl<'a, 'ctx> ExpressionChecker<'a, 'ctx> {
pub const fn new(ctx: &'a mut CheckerContext<'ctx>) -> Self {
Self {
ctx,
depth: DepthCounter::with_profile(RecursionProfile::ExpressionCheck),
}
}
pub fn check(&mut self, idx: NodeIndex) -> TypeId {
self.check_with_context(idx, None)
}
pub fn check_with_context(&mut self, idx: NodeIndex, context_type: Option<TypeId>) -> TypeId {
if !self.depth.enter() {
return TypeId::ERROR;
}
let result = if let Some(ctx_type) = context_type {
self.compute_type_with_context(idx, ctx_type)
} else {
if let Some(&cached) = self.ctx.node_types.get(&idx.0) {
self.depth.leave();
return cached;
}
let result = self.compute_type(idx);
self.ctx.node_types.insert(idx.0, result);
result
};
self.depth.leave();
result
}
pub fn compute_type_uncached(&mut self, idx: NodeIndex) -> TypeId {
self.compute_type_impl(idx, None)
}
fn compute_type_with_context(&mut self, idx: NodeIndex, context_type: TypeId) -> TypeId {
self.compute_type_impl(idx, Some(context_type))
}
fn compute_type(&mut self, idx: NodeIndex) -> TypeId {
self.compute_type_impl(idx, None)
}
fn compute_type_impl(&mut self, idx: NodeIndex, _context_type: Option<TypeId>) -> TypeId {
let Some(node) = self.ctx.arena.get(idx) else {
return TypeId::UNKNOWN;
};
match node.kind {
k if k == SyntaxKind::NullKeyword as u16 => TypeId::NULL,
k if k == syntax_kind_ext::TYPE_OF_EXPRESSION => TypeId::STRING,
k if k == syntax_kind_ext::VOID_EXPRESSION => TypeId::UNDEFINED,
k if k == syntax_kind_ext::PARENTHESIZED_EXPRESSION => {
if let Some(paren) = self.ctx.arena.get_parenthesized(node) {
if paren.expression.is_none() {
return TypeId::ERROR;
}
self.compute_type_impl(paren.expression, _context_type)
} else {
TypeId::DELEGATE
}
}
k if k == SyntaxKind::NumericLiteral as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::StringLiteral as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::TrueKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::FalseKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::NoSubstitutionTemplateLiteral as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::Identifier as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::ThisKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::SuperKeyword as u16 => TypeId::DELEGATE,
k if k == syntax_kind_ext::BINARY_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::CALL_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::NEW_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::PROPERTY_ACCESS_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::ELEMENT_ACCESS_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::CONDITIONAL_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::FUNCTION_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::ARROW_FUNCTION => TypeId::DELEGATE,
k if k == syntax_kind_ext::OBJECT_LITERAL_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::ARRAY_LITERAL_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::CLASS_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::PREFIX_UNARY_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::POSTFIX_UNARY_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::AWAIT_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::AS_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::SATISFIES_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::TYPE_ASSERTION => TypeId::DELEGATE,
k if k == syntax_kind_ext::TEMPLATE_EXPRESSION => TypeId::DELEGATE,
k if k == syntax_kind_ext::VARIABLE_DECLARATION => TypeId::DELEGATE,
k if k == syntax_kind_ext::FUNCTION_DECLARATION => TypeId::DELEGATE,
k if k == syntax_kind_ext::TYPE_REFERENCE => TypeId::DELEGATE,
k if k == syntax_kind_ext::UNION_TYPE => TypeId::DELEGATE,
k if k == syntax_kind_ext::INTERSECTION_TYPE => TypeId::DELEGATE,
k if k == syntax_kind_ext::ARRAY_TYPE => TypeId::DELEGATE,
k if k == syntax_kind_ext::TYPE_OPERATOR => TypeId::DELEGATE,
k if k == syntax_kind_ext::FUNCTION_TYPE => TypeId::DELEGATE,
k if k == syntax_kind_ext::TYPE_LITERAL => TypeId::DELEGATE,
k if k == syntax_kind_ext::TYPE_QUERY => TypeId::DELEGATE,
k if k == syntax_kind_ext::QUALIFIED_NAME => TypeId::DELEGATE,
k if k == SyntaxKind::NumberKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::StringKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::BooleanKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::VoidKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::AnyKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::NeverKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::UnknownKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::UndefinedKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::ObjectKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::BigIntKeyword as u16 => TypeId::DELEGATE,
k if k == SyntaxKind::SymbolKeyword as u16 => TypeId::DELEGATE,
k if k == syntax_kind_ext::JSX_ELEMENT => TypeId::DELEGATE,
k if k == syntax_kind_ext::JSX_SELF_CLOSING_ELEMENT => TypeId::DELEGATE,
k if k == syntax_kind_ext::JSX_FRAGMENT => TypeId::DELEGATE,
_ => TypeId::DELEGATE,
}
}
pub const fn context(&self) -> &CheckerContext<'ctx> {
self.ctx
}
}
#[cfg(test)]
#[path = "../tests/expr.rs"]
mod tests;