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