Skip to content

Commit 006c502

Browse files
hyperpolymathclaude
andcommitted
Implement Phase 1: manifest parsing, BQN codegen, CBQN FFI bridge, and ABI types
- Manifest: [project], [[patterns]] (loop-sum/map-transform/filter-predicate/ sort/group-by with input-type/output-type), [bqn] (backend: cbqn, optimize) - Codegen: parser.rs (analyse manifest patterns into ABI types), bqn_gen.rs (emit .bqn files with +´ fold, ¨ each, / replicate, ⍋⊏ grade+select, ⊔ group), ffi_gen.rs (CBQN FFI bridge: C header + Zig implementation) - ABI: BQNPrimitive (Join/Reverse/GradeUp/Replicate/Select/Reshape/Fold/Scan/ Each/Table), ArrayPattern, ArrayPatternKind, BQNProgram, FFIDeclaration - Tests: 16 integration tests covering manifest parsing, validation, pattern analysis, BQN generation, FFI generation, end-to-end pipeline, ABI properties - Example: examples/array-ops/ with all five pattern types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c0c23bf commit 006c502

11 files changed

Lines changed: 2520 additions & 45 deletions

File tree

Cargo.lock

Lines changed: 906 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/array-ops/bqniser.toml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# bqniser manifest — array-ops example
2+
# SPDX-License-Identifier: PMPL-1.0-or-later
3+
#
4+
# Demonstrates all five source-pattern types that bqniser can detect
5+
# and rewrite as BQN array primitives.
6+
7+
[project]
8+
name = "array-ops-example"
9+
version = "0.1.0"
10+
description = "Example: common array operations rewritten as BQN primitives"
11+
12+
# Pattern 1: Sum all prices — loop replaced by BQN fold (+´)
13+
[[patterns]]
14+
name = "sum-prices"
15+
source-pattern = "loop-sum"
16+
input-type = "f64"
17+
output-type = "f64"
18+
19+
# Pattern 2: Double each value — map replaced by BQN each (¨)
20+
[[patterns]]
21+
name = "double-values"
22+
source-pattern = "map-transform"
23+
input-type = "f64"
24+
output-type = "f64"
25+
26+
# Pattern 3: Keep positive numbers — filter replaced by BQN replicate (/)
27+
[[patterns]]
28+
name = "keep-positive"
29+
source-pattern = "filter-predicate"
30+
input-type = "f64"
31+
output-type = "Vec<f64>"
32+
33+
# Pattern 4: Sort ascending — replaced by BQN grade+select (⍋⊏)
34+
[[patterns]]
35+
name = "sort-ascending"
36+
source-pattern = "sort"
37+
input-type = "f64"
38+
output-type = "Vec<f64>"
39+
40+
# Pattern 5: Group by category — replaced by BQN group (⊔)
41+
[[patterns]]
42+
name = "group-by-category"
43+
source-pattern = "group-by"
44+
input-type = "f64"
45+
output-type = "Vec<Vec<f64>>"
46+
47+
[bqn]
48+
backend = "cbqn"
49+
optimize = true

src/abi/mod.rs

Lines changed: 235 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,240 @@
22
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
33
//
44
// ABI module for bqniser.
5+
//
56
// Rust-side types mirroring the Idris2 ABI formal definitions.
6-
// The Idris2 proofs guarantee correctness; this module provides runtime types.
7+
// The Idris2 proofs (in src/abi/*.idr) guarantee correctness of the
8+
// interface layout; this module provides runtime type representations
9+
// that the codegen layer uses to emit BQN programs and FFI bridges.
10+
11+
use serde::{Deserialize, Serialize};
12+
13+
// ---------------------------------------------------------------------------
14+
// BQN Primitives
15+
// ---------------------------------------------------------------------------
16+
17+
/// All BQN primitives that bqniser can emit.
18+
///
19+
/// Each variant carries the Unicode glyph, a human-readable description,
20+
/// and the arity (monadic = 1 argument, dyadic = 2 arguments).
21+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
22+
pub enum BQNPrimitive {
23+
/// `∾` — Join: concatenate arrays.
24+
Join,
25+
/// `⌽` — Reverse: reverse array element order.
26+
Reverse,
27+
/// `⍋` — Grade Up: produce ascending sort permutation.
28+
GradeUp,
29+
/// `/` — Replicate: filter by boolean mask or repeat by counts.
30+
Replicate,
31+
/// `⊏` — Select: index into an array.
32+
Select,
33+
/// `⥊` — Reshape: change the shape of an array.
34+
Reshape,
35+
/// `´` — Fold: reduce an array with a binary function.
36+
Fold,
37+
/// `` ` `` — Scan: prefix-scan (cumulative fold) over an array.
38+
Scan,
39+
/// `¨` — Each: apply a function to every element.
40+
Each,
41+
/// `⌜` — Table: outer-product / cartesian application.
42+
Table,
43+
}
44+
45+
impl BQNPrimitive {
46+
/// Return the BQN Unicode glyph for this primitive.
47+
pub fn glyph(self) -> &'static str {
48+
match self {
49+
BQNPrimitive::Join => "∾",
50+
BQNPrimitive::Reverse => "⌽",
51+
BQNPrimitive::GradeUp => "⍋",
52+
BQNPrimitive::Replicate => "/",
53+
BQNPrimitive::Select => "⊏",
54+
BQNPrimitive::Reshape => "⥊",
55+
BQNPrimitive::Fold => "´",
56+
BQNPrimitive::Scan => "`",
57+
BQNPrimitive::Each => "¨",
58+
BQNPrimitive::Table => "⌜",
59+
}
60+
}
61+
62+
/// Return a human-readable name.
63+
pub fn label(self) -> &'static str {
64+
match self {
65+
BQNPrimitive::Join => "Join",
66+
BQNPrimitive::Reverse => "Reverse",
67+
BQNPrimitive::GradeUp => "Grade Up",
68+
BQNPrimitive::Replicate => "Replicate",
69+
BQNPrimitive::Select => "Select",
70+
BQNPrimitive::Reshape => "Reshape",
71+
BQNPrimitive::Fold => "Fold",
72+
BQNPrimitive::Scan => "Scan",
73+
BQNPrimitive::Each => "Each",
74+
BQNPrimitive::Table => "Table",
75+
}
76+
}
77+
78+
/// Return the arity: 1 for monadic primitives, 2 for dyadic.
79+
///
80+
/// Modifiers (Fold, Scan, Each, Table) are technically 1-modifiers
81+
/// applied to a function, but they operate on arrays so we report
82+
/// their effective arity with the operand included.
83+
pub fn arity(self) -> u8 {
84+
match self {
85+
BQNPrimitive::Join => 2,
86+
BQNPrimitive::Reverse => 1,
87+
BQNPrimitive::GradeUp => 1,
88+
BQNPrimitive::Replicate => 2,
89+
BQNPrimitive::Select => 2,
90+
BQNPrimitive::Reshape => 2,
91+
BQNPrimitive::Fold => 1, // modifier applied to a function, then to array
92+
BQNPrimitive::Scan => 1,
93+
BQNPrimitive::Each => 1,
94+
BQNPrimitive::Table => 2,
95+
}
96+
}
97+
}
98+
99+
impl std::fmt::Display for BQNPrimitive {
100+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101+
write!(f, "{} ({})", self.label(), self.glyph())
102+
}
103+
}
104+
105+
// ---------------------------------------------------------------------------
106+
// Array Patterns
107+
// ---------------------------------------------------------------------------
108+
109+
/// Describes an array computation pattern detected in source code.
110+
///
111+
/// Links a user-visible name, the detected pattern family, and the
112+
/// BQN primitive(s) that will replace it.
113+
#[derive(Debug, Clone, Serialize, Deserialize)]
114+
pub struct ArrayPattern {
115+
/// User-chosen name (from the manifest [[patterns]] entry).
116+
pub name: String,
117+
118+
/// The kind of source-level pattern detected.
119+
pub kind: ArrayPatternKind,
120+
121+
/// The BQN primitive(s) used in the rewrite.
122+
/// Most rewrites use a single primitive; some (e.g. sort) compose two.
123+
pub primitives: Vec<BQNPrimitive>,
124+
125+
/// Element type flowing into the pattern (e.g. "f64").
126+
pub input_type: String,
127+
128+
/// Result type after the BQN rewrite (e.g. "f64", "Vec<f64>").
129+
pub output_type: String,
130+
}
131+
132+
/// Classification of source-level array patterns.
133+
///
134+
/// Mirrors `SourcePattern` from the manifest but lives in the ABI layer
135+
/// so that the Idris2 proofs can reference it.
136+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
137+
pub enum ArrayPatternKind {
138+
/// Accumulator loop that sums/reduces values.
139+
LoopSum,
140+
/// Map/transform applying a function element-wise.
141+
MapTransform,
142+
/// Filter selecting elements matching a predicate.
143+
FilterPredicate,
144+
/// Sorting by a key or comparison function.
145+
Sort,
146+
/// Group-by aggregating elements by a classifier.
147+
GroupBy,
148+
}
149+
150+
impl ArrayPatternKind {
151+
/// Return the primary BQN primitive(s) for this pattern kind.
152+
///
153+
/// Some patterns compose multiple primitives:
154+
/// - LoopSum -> [Fold] with +
155+
/// - MapTransform -> [Each]
156+
/// - FilterPredicate -> [Replicate]
157+
/// - Sort -> [GradeUp, Select]
158+
/// - GroupBy -> each element's group via / or ⊔
159+
pub fn primary_primitives(self) -> Vec<BQNPrimitive> {
160+
match self {
161+
ArrayPatternKind::LoopSum => vec![BQNPrimitive::Fold],
162+
ArrayPatternKind::MapTransform => vec![BQNPrimitive::Each],
163+
ArrayPatternKind::FilterPredicate => vec![BQNPrimitive::Replicate],
164+
ArrayPatternKind::Sort => vec![BQNPrimitive::GradeUp, BQNPrimitive::Select],
165+
ArrayPatternKind::GroupBy => vec![BQNPrimitive::Replicate, BQNPrimitive::Each],
166+
}
167+
}
168+
}
169+
170+
// ---------------------------------------------------------------------------
171+
// BQN Program
172+
// ---------------------------------------------------------------------------
173+
174+
/// A complete BQN program ready for emission.
175+
///
176+
/// Holds the source text, metadata about which patterns it implements,
177+
/// and the CBQN FFI function signatures needed to call it from native code.
178+
#[derive(Debug, Clone, Serialize, Deserialize)]
179+
pub struct BQNProgram {
180+
/// The project name this program belongs to.
181+
pub project_name: String,
182+
183+
/// BQN source code (Unicode).
184+
pub source: String,
185+
186+
/// The array patterns this program implements.
187+
pub patterns: Vec<ArrayPattern>,
188+
189+
/// CBQN FFI function declarations for calling this program.
190+
pub ffi_declarations: Vec<FFIDeclaration>,
191+
192+
/// Whether optimisation was applied during generation.
193+
pub optimised: bool,
194+
}
195+
196+
/// A single FFI function declaration for the CBQN bridge.
197+
///
198+
/// Describes a C-callable function that invokes a BQN expression via CBQN.
199+
#[derive(Debug, Clone, Serialize, Deserialize)]
200+
pub struct FFIDeclaration {
201+
/// C function name (e.g. "bqniser_sum_values").
202+
pub c_name: String,
203+
204+
/// BQN expression this function evaluates.
205+
pub bqn_expr: String,
206+
207+
/// C parameter types (e.g. ["const double*", "size_t"]).
208+
pub param_types: Vec<String>,
209+
210+
/// C return type (e.g. "double").
211+
pub return_type: String,
212+
}
213+
214+
// ---------------------------------------------------------------------------
215+
// Conversion helpers
216+
// ---------------------------------------------------------------------------
217+
218+
/// Convert a manifest SourcePattern to the ABI ArrayPatternKind.
219+
pub fn source_pattern_to_kind(
220+
sp: &crate::manifest::SourcePattern,
221+
) -> ArrayPatternKind {
222+
match sp {
223+
crate::manifest::SourcePattern::LoopSum => ArrayPatternKind::LoopSum,
224+
crate::manifest::SourcePattern::MapTransform => ArrayPatternKind::MapTransform,
225+
crate::manifest::SourcePattern::FilterPredicate => ArrayPatternKind::FilterPredicate,
226+
crate::manifest::SourcePattern::Sort => ArrayPatternKind::Sort,
227+
crate::manifest::SourcePattern::GroupBy => ArrayPatternKind::GroupBy,
228+
}
229+
}
7230

8-
// TODO: Define BQN-specific ABI types and verification functions.
231+
/// Build an ArrayPattern from a manifest PatternEntry.
232+
pub fn pattern_from_entry(entry: &crate::manifest::PatternEntry) -> ArrayPattern {
233+
let kind = source_pattern_to_kind(&entry.source_pattern);
234+
ArrayPattern {
235+
name: entry.name.clone(),
236+
kind,
237+
primitives: kind.primary_primitives(),
238+
input_type: entry.input_type.clone(),
239+
output_type: entry.output_type.clone(),
240+
}
241+
}

0 commit comments

Comments
 (0)