Add call hooks for series and push

Introduces `CallHooks` to enable extensions to the type inference and
AST finalization process. This allows for more dynamic and configurable
behavior without hardcoding symbol names in the compiler core.

Specifically, this commit adds:

- **`series_post_call`**: A hook for the `(series n)` function. It
  ensures that the return type is a fresh `TypeVar` when the element
  type is `Any`, allowing for proper type inference from subsequent
  `push` calls.
- **`series_finalize`**: A hook for `(series n)` that rewrites the AST
  node. It replaces the generic `series` call with a pre-configured
  factory closure that allocates the correct series storage type (e.g.,
  `ScalarSeries<f64>` for floats, `RecordSeries` for records). This
  avoids runtime dispatch and relies on type information captured during
  compilation.
- **`push_post_call`**: A hook for the `(push series val)` function. It
  unifies the series element `TypeVar` with the pushed value's type. It
  also handles numeric and record field promotion and updates the
  `TypeVar` binding if a promotion occurs.
This commit is contained in:
2026-03-28 21:13:26 +01:00
parent 6cab8f649b
commit 8efd12add6
7 changed files with 408 additions and 205 deletions
+7 -2
View File
@@ -2,7 +2,7 @@ use crate::ast::nodes::{
Address, AnalyzedNode, GlobalIdx, IdentifierBinding, Node,
NodeKind, NodeMetrics, TypedNode, TypedPhase,
};
use crate::ast::types::{Identity, Purity};
use crate::ast::types::{Identity, Purity, Value};
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
@@ -70,7 +70,12 @@ impl<'a> Analyzer<'a> {
let mut is_recursive = false;
let (new_kind, purity) = match &node.kind {
NodeKind::Constant(v) => (NodeKind::Constant(v.clone()), Purity::Pure),
// Propagate the declared purity of function constants so the constant
// folder does not evaluate impure factory closures at compile time.
NodeKind::Constant(v) => {
let purity = if let Value::Function(f) = v { f.purity } else { Purity::Pure };
(NodeKind::Constant(v.clone()), purity)
}
NodeKind::Nop => (NodeKind::Nop, Purity::Pure),
NodeKind::Identifier { symbol, binding } => {