use std::borrow::Cow;
use std::rc::Rc;
use super::Expander;
use super::context::DefinitionContext;
use super::problems::ExpandProblems;
use super::recur_check::RecurCheck;
use super::rest_of_body::RestOfBody;
use super::rust_clauses::ClausedTemplate;
use super::shape::Shape;
use super::try_check::TryCheck;
use super::type_note::TypeNote;
use crate::core::{
Arm, Binder, BindingId, BindingKind, Block, BodyItem, Control, ControlForm, Expr, ExprKind,
ExpressionForm, FieldLabel, FieldSpelling, KernelForm, Keyword, Lambda, Leaf, LeafForm,
Literal, MapOperation, Namespace, ParsedTemplate, Pattern, PatternArguments, PatternKind,
PatternPart, ReferenceKind, RustTemplate, StructExpr, StructForm, TypeExpr, VariantInfo,
};
use crate::diagnostic::Diagnostic;
use crate::reader::{Form, FormKind, Symbol};
use crate::scope::{Scope, ScopeKind};
use crate::source::Span;
use crate::stack::DeepRecursion;
impl Expander<'_> {
const LABEL_AND_VALUE: usize = 2;
pub(super) fn lower_expr(&mut self, form: &Form) -> Result<Expr, Diagnostic> {
if form.as_list().is_some() && self.nesting_depth == Form::NESTING_LIMIT {
return Err(self.nests_too_deep(form.span()));
}
self.nesting_depth += 1;
let lowered = DeepRecursion::guard(|| self.lower_form(form));
self.nesting_depth -= 1;
lowered
}
pub(super) fn nests_too_deep(&self, span: Span) -> Diagnostic {
let origin = self.tables.macro_origins.get(span);
let expansion = origin.as_ref().map(|origin| &*origin.name);
ExpandProblems::expansion_nests_too_deep(span, Form::NESTING_LIMIT, expansion)
}
fn lower_form(&mut self, form: &Form) -> Result<Expr, Diagnostic> {
self.expansion.lowered_forms += 1;
let span = form.span();
let kind = match form.kind() {
FormKind::Symbol(symbol) => return self.lower_symbol(symbol, form),
FormKind::String(text) => {
ExprKind::Leaf(Leaf::Literal(Literal::String(Rc::clone(text))))
}
FormKind::Number(number) => {
ExprKind::Leaf(Leaf::Literal(Literal::Number(number.clone())))
}
FormKind::List(items) => return self.lower_list(form, items),
};
Ok(Expr::new(kind, span))
}
pub(super) fn is_kernel_form(&self, form: &Form, kernel: KernelForm) -> bool {
self.kernel_head(form) == Some(kernel)
}
pub(super) fn lower_lambda(
&mut self,
shape: &Shape,
params: &Form,
body: &[Form],
) -> Result<Lambda, Diagnostic> {
let scope = self.tables.scopes.fresh(ScopeKind::Local);
let mut params = shape.list(params)?.to_vec();
let mut body = body.to_vec();
params
.iter_mut()
.chain(body.iter_mut())
.for_each(|form| form.add_scope(scope));
let params = params
.iter()
.map(|param| self.lower_binder(param, None))
.collect::<Result<Vec<Binder>, Diagnostic>>()?;
let body = self.lower_body(&body)?;
RecurCheck::inside(params.len()).block(&body)?;
TryCheck::inside().block(&body)?;
Ok(Lambda::new(params, body))
}
pub(super) fn field_label(&self, shape: &Shape, form: &Form) -> Result<FieldLabel, Diagnostic> {
Ok(FieldLabel {
name: shape.symbol(form)?.shared_name(),
span: form.span(),
spelling: FieldSpelling::Bare,
})
}
pub(super) fn kernel_head(&self, form: &Form) -> Option<KernelForm> {
let head = form.as_list()?.first()?.as_symbol()?;
match self.kind(self.try_resolve(head, Namespace::Value)?) {
BindingKind::KernelForm(kernel) => Some(kernel),
_ => None,
}
}
fn lower_symbol(&mut self, symbol: &Symbol, form: &Form) -> Result<Expr, Diagnostic> {
let span = form.span();
if let Some(field_name) = Keyword::field_name(symbol.name()) {
return Ok(Expr::new(self.lower_field_reader(field_name, span), span));
}
let binding = self
.resolve(symbol, Namespace::Value, span)
.map_err(|unbound| self.noted_if_type(unbound, symbol))?;
if matches!(self.kind(binding), BindingKind::Macro(_)) {
return self.lower_macro_value(binding, symbol, form);
}
Ok(Expr::new(self.lower_reference(binding, span)?, span))
}
fn noted_if_type(&mut self, unbound: Diagnostic, symbol: &Symbol) -> Diagnostic {
let type_name = self.try_resolve(symbol, Namespace::Type).map(|_| symbol);
self.type_note.noted(unbound, type_name)
}
fn without_type_note<T>(&mut self, lower: impl FnOnce(&mut Self) -> T) -> T {
let outer = std::mem::replace(&mut self.type_note, TypeNote::shown());
let lowered = lower(self);
self.type_note = outer;
lowered
}
pub(super) fn with_fresh_type_note<T>(&mut self, lower: impl FnOnce(&mut Self) -> T) -> T {
let outer = std::mem::take(&mut self.type_note);
let lowered = lower(self);
self.type_note = outer;
lowered
}
fn written_binding(form: &Form) -> String {
match form.as_list() {
Some([_, binder, value]) => format!("{binder} {value}"),
_ => "name value".to_string(),
}
}
fn lower_macro_value(
&mut self,
binding: BindingId,
symbol: &Symbol,
form: &Form,
) -> Result<Expr, Diagnostic> {
match self.value_expansion(binding, form.clone())? {
Some(expanded) => self.lower_expansion(&expanded),
None => Err(self.macro_as_value(binding, symbol, form.span())),
}
}
fn macro_as_value(&mut self, binding: BindingId, symbol: &Symbol, span: Span) -> Diagnostic {
ExpandProblems::macro_as_value(
symbol.name(),
span,
self.macro_function(binding, symbol, span)
.map(|function| Rc::from(self.tables.bindings.get(function).name())),
)
}
fn lower_reference(&mut self, binding: BindingId, span: Span) -> Result<ExprKind, Diagnostic> {
match self.kind(binding) {
BindingKind::KernelForm(kernel) => Err(ExpandProblems::form_as_value(kernel, span)),
BindingKind::Boolean(value) => Ok(ExprKind::Leaf(Leaf::Literal(Literal::Bool(value)))),
BindingKind::Variant(_, info) if info.arity == 0 => {
Ok(ExprKind::Variant(binding, Vec::new()))
}
BindingKind::Variant(_, info) => Err(ExpandProblems::variant_as_value(
self.tables.bindings.get(binding).name(),
info.arity,
span,
)),
BindingKind::Local if self.tables.bindings.get(binding).name() == "_" => {
Err(ExpandProblems::ignored_binding_read(span))
}
_ => Ok(ExprKind::Leaf(Leaf::Var(binding))),
}
}
fn lower_list(&mut self, form: &Form, items: &[Form]) -> Result<Expr, Diagnostic> {
let Some(head) = items.first() else {
return Err(ExpandProblems::empty_call(form.span()));
};
if let Some(field_name) = head
.as_symbol()
.and_then(|symbol| Keyword::field_name(symbol.name()))
{
return self.lower_field_keyword_call(form, field_name);
}
if let Some(kernel) = self.kernel_head(form) {
return self.lower_kernel(kernel, form);
}
self.lower_call(form, items)
}
fn lower_call(&mut self, form: &Form, items: &[Form]) -> Result<Expr, Diagnostic> {
if let Some(binding) = self.macro_head(form) {
let expanded = self.apply_macro(binding, form.clone(), None)?;
return self.lower_expansion(&expanded);
}
if let Some(binding) = self.variant_head(form) {
return self.lower_variant_call(binding, form, items);
}
if let Some(failed) = self.failed_macro_use(form) {
return Ok(failed);
}
let callee = self.lower_callee(&items[0])?;
let arguments = self.lower_arguments(&items[1..])?;
Ok(Expr::new(
ExprKind::Call(Box::new(callee), arguments),
form.span(),
))
}
fn lower_callee(&mut self, head: &Form) -> Result<Expr, Diagnostic> {
if self.names_struct(head) {
return self.without_type_note(|expander| expander.lower_recovered(head));
}
self.lower_recovered(head)
}
fn names_struct(&self, head: &Form) -> bool {
head.as_symbol()
.and_then(|symbol| self.try_resolve(symbol, Namespace::Type))
.is_some_and(|binding| matches!(self.kind(binding), BindingKind::Struct(_)))
}
fn lower_variant_call(
&mut self,
binding: BindingId,
form: &Form,
items: &[Form],
) -> Result<Expr, Diagnostic> {
if self.reads_labels(binding, &items[1..]) {
return self.lower_labeled_variant(binding, form, items);
}
let arguments = self.lower_arguments(&items[1..])?;
Ok(Expr::new(
ExprKind::Variant(binding, arguments),
form.span(),
))
}
fn reads_labels(&self, binding: BindingId, arguments: &[Form]) -> bool {
arguments.iter().any(|form| Self::field_of(form).is_some())
&& (self.tables.variant_fields.contains_key(&binding)
|| self.mislabels_payload(binding, arguments))
}
fn mislabels_payload(&self, binding: BindingId, arguments: &[Form]) -> bool {
let wrong_count = matches!(
self.kind(binding),
BindingKind::Variant(_, info) if info.arity != arguments.len()
);
wrong_count
&& arguments.chunks(Self::LABEL_AND_VALUE).all(|pair| {
pair.len() == Self::LABEL_AND_VALUE && Self::field_of(&pair[0]).is_some()
})
}
fn lower_labeled_variant(
&mut self,
binding: BindingId,
form: &Form,
items: &[Form],
) -> Result<Expr, Diagnostic> {
let fields = items[1..]
.chunks(Self::LABEL_AND_VALUE)
.map(|pair| self.lower_labeled_value(binding, &items[0], pair))
.collect::<Result<Vec<_>, Diagnostic>>()?;
Ok(Expr::new(
ExprKind::LabeledVariant(binding, fields),
form.span(),
))
}
fn lower_labeled_value(
&mut self,
binding: BindingId,
head: &Form,
pair: &[Form],
) -> Result<(FieldLabel, Expr), Diagnostic> {
let field = Self::field_of(&pair[0]);
match (field, pair.get(1)) {
(Some(field), Some(value)) => Ok((
Self::keyword_label(field, pair[0].span()),
self.lower_recovered(value)?,
)),
(Some(_), None) => Err(ExpandProblems::label_without_value(
head,
&self.tables.variant_fields[&binding],
pair[0].span(),
)),
(None, _) => Err(ExpandProblems::value_without_label(
head,
&self.tables.variant_fields[&binding],
pair[0].span(),
)),
}
}
fn field_of(form: &Form) -> Option<&str> {
Keyword::field_name(form.as_symbol()?.name())
}
fn lower_arguments(&mut self, arguments: &[Form]) -> Result<Vec<Expr>, Diagnostic> {
arguments
.iter()
.map(|argument| self.lower_recovered(argument))
.collect()
}
fn variant_head(&self, form: &Form) -> Option<BindingId> {
let items = form.as_list()?;
let head = items.first()?.as_symbol()?;
let binding = self.try_resolve(head, Namespace::Value)?;
matches!(
self.kind(binding),
BindingKind::Variant(_, VariantInfo { arity, .. })
if arity > 0 || self.reads_labels(binding, &items[1..])
)
.then_some(binding)
}
pub(super) fn lower_recovered(&mut self, form: &Form) -> Result<Expr, Diagnostic> {
match self.lower_expr(form) {
Ok(expr) => Ok(expr),
Err(diagnostic) => self.recover(diagnostic, form.span()),
}
}
fn recover(&mut self, diagnostic: Diagnostic, span: Span) -> Result<Expr, Diagnostic> {
self.recovered.keep(diagnostic, self.phase)?;
Ok(Self::placeholder(span))
}
pub(super) fn placeholder(span: Span) -> Expr {
Expr::new(ExprKind::Leaf(Leaf::Literal(Literal::Bool(false))), span)
}
fn lower_kernel(&mut self, kernel: KernelForm, form: &Form) -> Result<Expr, Diagnostic> {
let span = form.span();
let kind = match kernel {
KernelForm::Expression(expression) => {
self.lower_expression_form(expression, &Shape::new(kernel, form))
}
KernelForm::Local => Err(ExpandProblems::local_outside_body(
&Self::written_binding(form),
span,
)),
KernelForm::Macro => Err(ExpandProblems::macro_outside_def(span)),
KernelForm::Definition(_) => Err(self.misplaced_definition(kernel, span)),
};
kind.map(|kind| Expr::new(kind, span))
}
fn lower_expression_form(
&mut self,
expression: ExpressionForm,
shape: &Shape,
) -> Result<ExprKind, Diagnostic> {
match expression {
ExpressionForm::Fn => self.lower_fn(shape),
ExpressionForm::Recur => self.lower_recur(shape),
ExpressionForm::Try => self.lower_try(shape),
ExpressionForm::Infer => self.lower_infer(shape),
other => self.lower_value_form(other, shape),
}
}
fn lower_value_form(
&mut self,
expression: ExpressionForm,
shape: &Shape,
) -> Result<ExprKind, Diagnostic> {
match expression {
ExpressionForm::Leaf(form) => self.lower_leaf_form(form, shape),
ExpressionForm::Borrow(kind) => self.lower_borrow(kind, shape),
other => self.lower_other_expression(other, shape),
}
}
fn lower_other_expression(
&mut self,
expression: ExpressionForm,
shape: &Shape,
) -> Result<ExprKind, Diagnostic> {
match expression {
ExpressionForm::MapAt => self.lower_map_at(shape),
ExpressionForm::SetAt => self.lower_set_at(shape),
other => self.lower_simple_expression(other, shape),
}
}
fn lower_simple_expression(
&mut self,
expression: ExpressionForm,
shape: &Shape,
) -> Result<ExprKind, Diagnostic> {
match expression {
ExpressionForm::Deref => self.lower_deref(shape),
ExpressionForm::Control(form) => self.lower_control_form(form, shape),
ExpressionForm::Struct(form) => self.lower_struct_form(form, shape),
ExpressionForm::Match => self.lower_match(shape),
ExpressionForm::Assoc => self.lower_assoc(shape),
_ => unreachable!("the caller matches a simple expression form"),
}
}
fn lower_leaf_form(&mut self, form: LeafForm, shape: &Shape) -> Result<ExprKind, Diagnostic> {
match form {
LeafForm::Quote => self.lower_quote(shape),
LeafForm::Rust => self.lower_rust(shape),
}
}
fn lower_control_form(
&mut self,
form: ControlForm,
shape: &Shape,
) -> Result<ExprKind, Diagnostic> {
match form {
ControlForm::Do => self.lower_do(shape),
ControlForm::If => self.lower_if(shape),
}
}
fn lower_borrow(&mut self, kind: ReferenceKind, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.exactly(2)?;
let value = self.lower_expr(&items[1])?;
Ok(ExprKind::Borrow(kind, Box::new(value)))
}
fn lower_deref(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.exactly(2)?;
let value = self.lower_expr(&items[1])?;
Ok(ExprKind::Deref(Box::new(value)))
}
fn lower_struct_form(
&mut self,
form: StructForm,
shape: &Shape,
) -> Result<ExprKind, Diagnostic> {
match form {
StructForm::New => self.lower_new(shape),
StructForm::Field => self.lower_field_access(shape),
}
}
fn lower_fn(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.at_least(2)?;
let lambda = self.lower_lambda(shape, &items[1], &items[2..])?;
Ok(ExprKind::Fn(Rc::new(lambda)))
}
fn lower_do(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let body = self.lower_body(&shape.items()[1..])?;
Ok(ExprKind::Control(Control::Block(body)))
}
fn lower_quote(&self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let quoted = self.tables.scopes.quoted(&shape.exactly(2)?[1]);
Ok(ExprKind::Leaf(Leaf::Quote(quoted)))
}
pub(super) fn lower_body(&mut self, forms: &[Form]) -> Result<Block, Diagnostic> {
let mut context = DefinitionContext::default();
let items = self.lower_body_items(&mut RestOfBody::new(forms), &mut context)?;
Ok(Block { items })
}
pub(super) fn lower_body_items(
&mut self,
rest_of_body: &mut RestOfBody,
context: &mut DefinitionContext,
) -> Result<Vec<BodyItem>, Diagnostic> {
let failed = self.failed_body_definitions.len();
let items = self.lower_each_body_item(rest_of_body, context);
self.forget_failed_body_definitions(failed);
items
}
fn lower_each_body_item(
&mut self,
rest_of_body: &mut RestOfBody,
context: &mut DefinitionContext,
) -> Result<Vec<BodyItem>, Diagnostic> {
let mut items = Vec::new();
while let Some(form) = rest_of_body.next() {
let item = self.with_fresh_type_note(|expander| {
expander.lower_body_item(form, rest_of_body, context)
});
items.extend(item?);
}
Ok(items)
}
fn lower_body_item(
&mut self,
form: Cow<Form>,
rest_of_body: &mut RestOfBody,
context: &mut DefinitionContext,
) -> Result<Vec<BodyItem>, Diagnostic> {
let span = form.span();
self.declared_definitions.clear();
match self.expand_body_head(form, context) {
Ok(form) => Ok(vec![self.lower_body_form(&form, rest_of_body, context)?]),
Err(diagnostic) => {
let failure = self.recover(diagnostic, span)?;
Ok(self.bind_failed_body_names(&failure, rest_of_body, context))
}
}
}
fn lower_body_form(
&mut self,
form: &Form,
rest_of_body: &mut RestOfBody,
context: &mut DefinitionContext,
) -> Result<BodyItem, Diagnostic> {
let item = match self.kernel_head(form) {
Some(kernel @ KernelForm::Local) => {
self.lower_let(&Shape::new(kernel, form), rest_of_body, context)
}
Some(Self::DEF_FORM) => self.lower_misplaced_def(form, rest_of_body, context),
Some(Self::STRUCT_FORM) => self.lower_misplaced_struct(form, rest_of_body, context),
Some(Self::DO_FORM) if self.holds_definition(form) => {
self.lower_spliced_do(form, rest_of_body, context)
}
_ => return Ok(BodyItem::Expr(self.lower_recovered(form)?)),
};
self.recover_body_item(item, form.span())
}
fn expand_body_head<'form>(
&mut self,
form: Cow<'form, Form>,
context: &mut DefinitionContext,
) -> Result<Cow<'form, Form>, Diagnostic> {
if self.macro_head(&form).is_none() {
return Ok(form);
}
self.expand_head(form.into_owned(), Some(context))
.map(Cow::Owned)
}
fn recover_body_item(
&mut self,
item: Result<BodyItem, Diagnostic>,
span: Span,
) -> Result<BodyItem, Diagnostic> {
match item {
Ok(item) => Ok(item),
Err(diagnostic) => Ok(BodyItem::Expr(self.recover(diagnostic, span)?)),
}
}
fn lower_let(
&mut self,
shape: &Shape,
rest_of_body: &mut RestOfBody,
context: &DefinitionContext,
) -> Result<BodyItem, Diagnostic> {
let items = shape.exactly(3)?;
let value = self.lower_recovered(&items[2])?;
let scope = self.tables.scopes.fresh(ScopeKind::Local);
rest_of_body.add_scope(scope);
Ok(BodyItem::Let(
self.lower_binder(&Self::scoped(&items[1], scope), Some(context))?,
value,
))
}
pub(super) fn scoped(form: &Form, scope: Scope) -> Form {
let mut scoped = form.clone();
scoped.add_scope(scope);
scoped
}
pub(super) fn lower_binder(
&mut self,
form: &Form,
context: Option<&DefinitionContext>,
) -> Result<Binder, Diagnostic> {
let (name, annotation) = Self::split_binder(form);
let annotation = annotation
.map(|annotation| self.lower_type(annotation))
.transpose()?;
let binding = self.define_local(name, Namespace::Value, BindingKind::Local, context)?;
Ok(Binder {
binding,
annotation,
span: form.span(),
})
}
fn lower_if(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
if shape.items().len() == 3 {
return Err(shape.malformed_form_with_help(
"write `(when test body ...)` to run forms only when the test holds",
));
}
let items = shape.exactly(4)?;
let test = self.lower_recovered(&items[1])?;
let then = self.lower_recovered(&items[2])?;
let otherwise = self.lower_recovered(&items[3])?;
Ok(ExprKind::Control(Control::If(
Box::new(test),
Box::new(then),
Box::new(otherwise),
)))
}
fn lower_new(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.at_least(2)?;
if items[1]
.as_symbol()
.is_some_and(|symbol| symbol.name() == "_")
{
return self.lower_new_update(shape);
}
self.lower_new_construction(shape)
}
fn lower_new_update(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.at_least(3)?;
let base = self.lower_expr(&items[2])?;
let fields = self.lower_fields(shape, &items[3..])?;
Ok(ExprKind::Struct(StructExpr::Update(Box::new(base), fields)))
}
fn lower_new_construction(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.at_least(2)?;
let name = shape.symbol(&items[1])?;
let binding = self.resolve(name, Namespace::Type, items[1].span())?;
if !matches!(self.kind(binding), BindingKind::Struct(_)) {
return Err(ExpandProblems::wrong_namespace(
name.name(),
"a struct",
items[1].span(),
));
}
let fields = self.lower_fields(shape, &items[2..])?;
Ok(ExprKind::Struct(StructExpr::New(binding, fields)))
}
fn lower_fields(
&mut self,
shape: &Shape,
fields: &[Form],
) -> Result<Vec<(FieldLabel, Expr)>, Diagnostic> {
fields
.iter()
.map(|field| self.lower_field_value(shape, field))
.collect()
}
fn lower_assoc(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.exactly(4)?;
let value = self.lower_expr(&items[1])?;
let keys = shape
.list(&items[2])?
.iter()
.map(|key| self.lower_expr(key))
.collect::<Result<Vec<_>, Diagnostic>>()?;
let item = self.lower_expr(&items[3])?;
Ok(ExprKind::Assoc(Box::new(value), keys, Box::new(item)))
}
fn lower_map_at(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.at_least(3)?;
let operation = items[1]
.as_symbol()
.and_then(|symbol| MapOperation::of(symbol.name()))
.ok_or_else(|| shape.malformed(&items[1]))?;
let value = self.lower_expr(&items[2])?;
let arguments = items[3..]
.iter()
.map(|argument| self.lower_expr(argument))
.collect::<Result<Vec<_>, Diagnostic>>()?;
Ok(ExprKind::MapAt(operation, Box::new(value), arguments))
}
fn lower_set_at(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.exactly(4)?;
let value = self.lower_expr(&items[1])?;
let keys = shape
.list(&items[2])?
.iter()
.map(|key| self.lower_expr(key))
.collect::<Result<Vec<_>, Diagnostic>>()?;
let item = self.lower_expr(&items[3])?;
Ok(ExprKind::SetAt(Box::new(value), keys, Box::new(item)))
}
fn lower_field_value(
&mut self,
shape: &Shape,
field: &Form,
) -> Result<(FieldLabel, Expr), Diagnostic> {
let [label, value] = shape.list(field)? else {
return Err(shape.malformed(field));
};
Ok((
self.initializer_label(shape, label)?,
self.lower_recovered(value)?,
))
}
fn initializer_label(&self, shape: &Shape, label: &Form) -> Result<FieldLabel, Diagnostic> {
match label
.as_symbol()
.and_then(|symbol| Keyword::field_name(symbol.name()))
{
Some(field_name) => Ok(Self::keyword_label(field_name, label.span())),
None => self.field_label(shape, label),
}
}
fn lower_field_access(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.exactly(3)?;
let value = self.lower_recovered(&items[1])?;
Ok(ExprKind::Struct(StructExpr::Field(
Box::new(value),
self.field_label(shape, &items[2])?,
)))
}
fn lower_match(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.at_least(3)?;
let value = self.lower_recovered(&items[1])?;
let arms = items[2..]
.iter()
.map(|arm| self.lower_arm(shape, arm))
.collect::<Result<Vec<Arm>, Diagnostic>>()?;
Ok(ExprKind::Control(Control::Match(Box::new(value), arms)))
}
fn lower_arm(&mut self, shape: &Shape, arm: &Form) -> Result<Arm, Diagnostic> {
let items = shape.list(arm)?;
let (pattern, body) = items.split_first().ok_or_else(|| shape.malformed(arm))?;
let scope = self.tables.scopes.fresh(ScopeKind::Local);
let mut pattern = pattern.clone();
let mut body = body.to_vec();
pattern.add_scope(scope);
body.iter_mut().for_each(|form| form.add_scope(scope));
let pattern = self.lower_pattern(&pattern)?;
let body = self.lower_body(&body)?;
Ok(Arm {
pattern,
body,
span: arm.span(),
})
}
fn lower_pattern(&mut self, form: &Form) -> Result<Pattern, Diagnostic> {
let span = form.span();
let kind = match form.kind() {
FormKind::Symbol(symbol) => self.lower_symbol_pattern(form, symbol)?,
FormKind::List(items) => self.lower_list_pattern(items, span)?,
FormKind::Number(number) => PatternKind::Literal(Literal::Number(number.clone())),
FormKind::String(text) => PatternKind::Literal(Literal::String(Rc::clone(text))),
};
Ok(Pattern::new(kind, span))
}
fn lower_symbol_pattern(
&mut self,
form: &Form,
symbol: &Symbol,
) -> Result<PatternKind, Diagnostic> {
if symbol.name() == "_" {
return Ok(PatternKind::Wildcard);
}
if let Some(kind) = self.named_symbol_pattern(form, symbol)? {
return Ok(kind);
}
let outer = self.try_resolve(symbol, Namespace::Value);
let candidates = self.nullary_variant_candidates(symbol);
let binding = self.define_local(form, Namespace::Value, BindingKind::Local, None)?;
if candidates.is_empty() {
return Ok(PatternKind::Bind(binding));
}
Ok(PatternKind::BareName {
name: symbol.shared_name(),
binding,
candidates,
outer,
})
}
fn named_symbol_pattern(
&self,
form: &Form,
symbol: &Symbol,
) -> Result<Option<PatternKind>, Diagnostic> {
let Some(binding) = self.try_resolve(symbol, Namespace::Value) else {
return Ok(None);
};
match self.kind(binding) {
BindingKind::Boolean(value) => Ok(Some(PatternKind::Literal(Literal::Bool(value)))),
BindingKind::Variant(_, info) if info.arity == 0 => Ok(Some(PatternKind::Variant(
binding,
PatternArguments::default(),
))),
BindingKind::Variant(_, _) => Err(ExpandProblems::not_a_pattern(form.span())),
_ => Ok(None),
}
}
fn lower_list_pattern(
&mut self,
items: &[Form],
span: Span,
) -> Result<PatternKind, Diagnostic> {
let Some(head) = items.first().and_then(Form::as_symbol) else {
return Err(ExpandProblems::not_a_pattern(span));
};
if let Some(binding) = self.variant_pattern_head(head) {
return self.variant_pattern(binding, &items[1..]);
}
if let Some(binding) = self.struct_pattern_head(head) {
return self.struct_pattern(binding, &items[1..]);
}
self.short_variant_pattern(head, &items[1..], span)
}
pub(super) fn names_pattern_head(&self, head: &Symbol) -> bool {
self.variant_pattern_head(head).is_some()
|| self.struct_pattern_head(head).is_some()
|| !self.variant_candidates(head).is_empty()
}
fn variant_pattern_head(&self, head: &Symbol) -> Option<BindingId> {
let binding = self.try_resolve(head, Namespace::Value)?;
let is_variant = matches!(self.kind(binding), BindingKind::Variant(_, _));
(is_variant || self.tables.globals.is_failed_definition(binding)).then_some(binding)
}
fn struct_pattern_head(&self, head: &Symbol) -> Option<BindingId> {
let binding = self.try_resolve(head, Namespace::Type)?;
matches!(self.kind(binding), BindingKind::Struct(_)).then_some(binding)
}
fn variant_pattern(
&mut self,
binding: BindingId,
arguments: &[Form],
) -> Result<PatternKind, Diagnostic> {
Ok(PatternKind::Variant(
binding,
self.lower_pattern_arguments(arguments)?,
))
}
fn struct_pattern(
&mut self,
binding: BindingId,
arguments: &[Form],
) -> Result<PatternKind, Diagnostic> {
Ok(PatternKind::Struct(
binding,
self.lower_pattern_arguments(arguments)?,
))
}
fn short_variant_pattern(
&mut self,
head: &Symbol,
arguments: &[Form],
span: Span,
) -> Result<PatternKind, Diagnostic> {
let candidates = self.variant_candidates(head);
if candidates.is_empty() {
return Err(ExpandProblems::not_a_pattern(span));
}
Ok(PatternKind::ShortVariant {
name: head.shared_name(),
candidates,
arguments: self.lower_pattern_arguments(arguments)?,
})
}
fn lower_pattern_arguments(&mut self, forms: &[Form]) -> Result<PatternArguments, Diagnostic> {
let mut parts = Vec::with_capacity(forms.len());
let mut remaining = forms;
while let Some((form, rest)) = remaining.split_first() {
let (part, after) = match Self::written_field_keyword(form) {
Some(field_name) => self.lower_labeled_part(form, field_name, rest)?,
None => (PatternPart::Positional(self.lower_pattern(form)?), rest),
};
parts.push(part);
remaining = after;
}
Ok(PatternArguments { parts })
}
fn lower_labeled_part<'f>(
&mut self,
keyword: &Form,
field_name: &str,
rest: &'f [Form],
) -> Result<(PatternPart, &'f [Form]), Diagnostic> {
let label = Self::keyword_label(field_name, keyword.span());
match rest.split_first() {
Some((pattern, after)) if Self::written_field_keyword(pattern).is_none() => {
let pattern = self.lower_pattern(pattern)?;
Ok((PatternPart::Labeled(label, Some(pattern)), after))
}
_ => Ok((PatternPart::Labeled(label, None), rest)),
}
}
fn variant_candidates(&self, symbol: &Symbol) -> Vec<BindingId> {
self.visible_variants_ending_in(symbol, |_| true)
}
fn nullary_variant_candidates(&self, symbol: &Symbol) -> Vec<BindingId> {
self.visible_variants_ending_in(symbol, |info| info.arity == 0)
}
fn visible_variants_ending_in(
&self,
symbol: &Symbol,
accepts: impl Fn(VariantInfo) -> bool,
) -> Vec<BindingId> {
let mut variants: Vec<BindingId> = self
.tables
.table
.visible_qualified_bindings_ending_in(symbol, Namespace::Value, self.phase)
.into_iter()
.filter(|binding| {
matches!(self.kind(*binding), BindingKind::Variant(_, info) if accepts(info))
})
.collect();
variants.sort();
variants.dedup();
variants
}
fn lower_rust(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.at_least(Self::RUST_PARTS)?;
let text = shape.string(&items[1])?;
let template = Self::parse_rust_template(text, items[1].span())?;
let type_expr = self.lower_type(&items[2])?;
self.check_template_type_placeholders(&template, &type_expr, items[1].span())?;
let claused = ClausedTemplate {
parsed: &template,
span: items[1].span(),
type_expr: &type_expr,
type_span: items[2].span(),
};
let clauses = self.lower_rust_clauses(shape, &claused, &items[Self::RUST_PARTS..])?;
Ok(ExprKind::Leaf(Leaf::Rust(Rc::new(RustTemplate {
template: text.into(),
type_expr,
clauses,
}))))
}
pub(super) fn check_template_type_placeholders(
&self,
template: &ParsedTemplate,
type_expr: &TypeExpr,
span: Span,
) -> Result<(), Diagnostic> {
let parameters: Vec<String> = Self::where_binders(type_expr)
.iter()
.map(|binder| self.tables.bindings.get(binder.variable).name().to_string())
.collect();
for name in template.type_placeholders() {
if !parameters.iter().any(|parameter| parameter == name) {
return Err(ExpandProblems::unknown_type_placeholder(
name,
¶meters,
span,
));
}
}
Ok(())
}
fn lower_recur(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let arguments = shape.items()[1..]
.iter()
.map(|argument| self.lower_recovered(argument))
.collect::<Result<Vec<Expr>, Diagnostic>>()?;
Ok(ExprKind::Recur(arguments))
}
fn lower_try(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.exactly(2)?;
let value = self.lower_recovered(&items[1])?;
Ok(ExprKind::Try(Box::new(value)))
}
fn lower_infer(&mut self, shape: &Shape) -> Result<ExprKind, Diagnostic> {
let items = shape.exactly(2)?;
let operand = self.lower_recovered(&items[1])?;
Ok(ExprKind::Infer(Box::new(operand)))
}
}