Skip to main content

shape_ast/ast/
functions.rs

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