Skip to main content

shape_ast/ast/
functions.rs

1//! Function definition and parameter types for Shape AST
2
3use super::expressions::Expr;
4use super::span::Span;
5use super::statements::Statement;
6use super::types::TypeAnnotation;
7use serde::{Deserialize, Serialize};
8// Re-export TypeParam from types to avoid duplication
9pub use super::types::TypeParam;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct FunctionDef {
13    pub name: String,
14    pub name_span: Span,
15    pub type_params: Option<Vec<TypeParam>>,
16    pub params: Vec<FunctionParameter>,
17    pub return_type: Option<TypeAnnotation>,
18    pub where_clause: Option<Vec<super::types::WherePredicate>>,
19    pub body: Vec<Statement>,
20    pub annotations: Vec<Annotation>,
21    pub is_async: bool,
22    /// Whether this function is compile-time-only (`comptime fn`).
23    ///
24    /// Comptime-only functions can only be called from comptime contexts.
25    #[serde(default)]
26    pub is_comptime: bool,
27}
28
29/// A foreign function definition: `fn <language> name(params) -> type { foreign_body }`
30///
31/// The body is raw source text in the foreign language, not parsed as Shape.
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub struct ForeignFunctionDef {
34    /// The language identifier (e.g., "python", "julia", "sql")
35    pub language: String,
36    pub language_span: Span,
37    pub name: String,
38    pub name_span: Span,
39    pub type_params: Option<Vec<TypeParam>>,
40    pub params: Vec<FunctionParameter>,
41    pub return_type: Option<TypeAnnotation>,
42    /// The raw dedented source text of the foreign function body.
43    pub body_text: String,
44    /// Span of the body text in the original Shape source file.
45    pub body_span: Span,
46    pub annotations: Vec<Annotation>,
47    #[serde(default)]
48    pub is_async: bool,
49    /// Native ABI metadata for `extern "C"` declarations.
50    ///
51    /// When present, this foreign function is not compiled/invoked through a
52    /// language runtime extension. The VM links and invokes it via the native
53    /// C ABI path.
54    #[serde(default)]
55    pub native_abi: Option<NativeAbiBinding>,
56}
57
58/// Native ABI link metadata attached to a foreign function declaration.
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
60pub struct NativeAbiBinding {
61    /// ABI name (currently `"C"`).
62    pub abi: String,
63    /// Library path or logical dependency key.
64    pub library: String,
65    /// Symbol name to resolve in the library.
66    pub symbol: String,
67}
68
69impl ForeignFunctionDef {
70    /// Whether the declared return type is `Result<T>`.
71    pub fn returns_result(&self) -> bool {
72        matches!(
73            &self.return_type,
74            Some(TypeAnnotation::Generic { name, .. }) if name == "Result"
75        )
76    }
77
78    /// Whether this function uses native ABI binding (e.g. `extern "C"`).
79    pub fn is_native_abi(&self) -> bool {
80        self.native_abi.is_some()
81    }
82
83    /// Validate that all parameter and return types are explicitly annotated,
84    /// and that dynamic-language foreign functions declare `Result<T>` as their
85    /// return type.
86    ///
87    /// Foreign function bodies are opaque — the type system cannot infer types
88    /// from them. This returns a list of `(message, span)` for each problem,
89    /// shared between the compiler and the LSP.
90    ///
91    /// `dynamic_language` should be `true` for languages like Python, JS, Ruby
92    /// where every call can fail at runtime.  Currently all foreign languages
93    /// are treated as dynamic (the ABI declares this via `ErrorModel`).
94    pub fn validate_type_annotations(&self, dynamic_language: bool) -> Vec<(String, Span)> {
95        let mut errors = Vec::new();
96
97        for param in &self.params {
98            if param.type_annotation.is_none() {
99                let name = param.simple_name().unwrap_or("_");
100                errors.push((
101                    format!(
102                        "Foreign function '{}': parameter '{}' requires a type annotation \
103                         (type inference is not available for foreign function bodies)",
104                        self.name, name
105                    ),
106                    param.span(),
107                ));
108            }
109        }
110
111        if self.return_type.is_none() {
112            errors.push((
113                format!(
114                    "Foreign function '{}' requires an explicit return type annotation \
115                     (type inference is not available for foreign function bodies)",
116                    self.name
117                ),
118                self.name_span,
119            ));
120        } else if dynamic_language && !self.returns_result() {
121            let inner_type = self
122                .return_type
123                .as_ref()
124                .map(|t| t.to_type_string())
125                .unwrap_or_else(|| "T".to_string());
126            errors.push((
127                format!(
128                    "Foreign function '{}': return type must be Result<{}> \
129                     (dynamic language runtimes can fail on every call)",
130                    self.name, inner_type
131                ),
132                self.name_span,
133            ));
134        }
135
136        errors
137    }
138}
139
140#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
141pub struct FunctionParameter {
142    pub pattern: super::patterns::DestructurePattern,
143    #[serde(default)]
144    pub is_const: bool,
145    #[serde(default)]
146    pub is_reference: bool,
147    /// Whether this is an exclusive (mutable) reference: `&mut x`
148    /// Only meaningful when `is_reference` is true.
149    #[serde(default)]
150    pub is_mut_reference: bool,
151    pub type_annotation: Option<TypeAnnotation>,
152    pub default_value: Option<Expr>,
153}
154
155impl FunctionParameter {
156    /// Get the simple parameter name if this is a simple identifier pattern
157    pub fn simple_name(&self) -> Option<&str> {
158        self.pattern.as_identifier()
159    }
160
161    /// Get all identifiers bound by this parameter (for destructuring patterns)
162    pub fn get_identifiers(&self) -> Vec<String> {
163        self.pattern.get_identifiers()
164    }
165
166    /// Get the span for this parameter
167    pub fn span(&self) -> Span {
168        match &self.pattern {
169            super::patterns::DestructurePattern::Identifier(_, span) => *span,
170            super::patterns::DestructurePattern::Array(_) => Span::default(),
171            super::patterns::DestructurePattern::Object(_) => Span::default(),
172            super::patterns::DestructurePattern::Rest(_) => Span::default(),
173            super::patterns::DestructurePattern::Decomposition(_) => Span::default(),
174        }
175    }
176}
177
178// Note: TypeParam is re-exported from types module above
179
180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
181pub struct Annotation {
182    pub name: String,
183    pub args: Vec<Expr>,
184    pub span: Span,
185}
186
187impl Annotation {
188    pub fn get<'a>(annotations: &'a [Annotation], name: &str) -> Option<&'a Annotation> {
189        annotations.iter().find(|a| a.name == name)
190    }
191}
192
193/// Annotation definition with lifecycle hooks
194///
195/// Annotations are Shape's aspect-oriented programming mechanism.
196/// They can define handlers for different lifecycle events:
197///
198/// ```shape
199/// annotation pattern() {
200///     on_define(fn, ctx) { ctx.registry("patterns").set(fn.name, fn); }
201///     metadata() { return { is_pattern: true }; }
202/// }
203/// ```
204#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub struct AnnotationDef {
206    pub name: String,
207    pub name_span: Span,
208    /// Annotation parameters (e.g., `period` in `@warmup(period)`)
209    pub params: Vec<FunctionParameter>,
210    /// Optional explicit target restrictions from `targets: [...]`.
211    /// If None, target applicability is inferred from handler kinds.
212    pub allowed_targets: Option<Vec<AnnotationTargetKind>>,
213    /// Lifecycle handlers (on_define, before, after, metadata)
214    pub handlers: Vec<AnnotationHandler>,
215    /// Full span of the annotation definition
216    pub span: Span,
217}
218
219/// Type of annotation lifecycle handler
220#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
221pub enum AnnotationHandlerType {
222    /// Called when function is defined (registration time)
223    OnDefine,
224    /// Called before each function invocation
225    Before,
226    /// Called after each function invocation
227    After,
228    /// Returns static metadata for tooling/optimization
229    Metadata,
230    /// Compile-time pre-inference handler: `comptime pre(target, ctx) { ... }`
231    /// Can emit directives to concretize untyped function parameters.
232    ComptimePre,
233    /// Compile-time post-inference handler: `comptime post(target, ctx) { ... }`
234    /// Can emit directives to synthesize return types and runtime bodies.
235    ComptimePost,
236}
237
238/// Describes what kind of syntax element an annotation is targeting.
239/// Used for compile-time validation of annotation applicability.
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
241pub enum AnnotationTargetKind {
242    /// @annotation before a function definition
243    Function,
244    /// @annotation before a type/struct/enum definition
245    Type,
246    /// @annotation before a module definition
247    Module,
248    /// @annotation before an arbitrary expression
249    Expression,
250    /// @annotation before a block expression
251    Block,
252    /// @annotation inside an await expression: `await @timeout(5s) expr`
253    AwaitExpr,
254    /// @annotation before a let/var/const binding
255    Binding,
256}
257
258/// A lifecycle handler within an annotation definition
259#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
260pub struct AnnotationHandler {
261    /// Type of handler (on_define, before, after, metadata)
262    pub handler_type: AnnotationHandlerType,
263    /// Handler parameters (e.g., `fn, ctx` for on_define)
264    pub params: Vec<AnnotationHandlerParam>,
265    /// Optional return type annotation
266    pub return_type: Option<TypeAnnotation>,
267    /// Handler body (a block expression)
268    pub body: Expr,
269    /// Span for error reporting
270    pub span: Span,
271}
272
273#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
274pub struct AnnotationHandlerParam {
275    pub name: String,
276    #[serde(default)]
277    pub is_variadic: bool,
278}