Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# 0.3.0 - 2025-11-04

* Add `array_fold` builtin function [#145](https://github.com/BlockstreamResearch/SimplicityHL/pull/145)
* Add getters for `Span` and improve error handling [#146](https://github.com/BlockstreamResearch/SimplicityHL/pull/146)
* Add VSCode extension with LSP support
[#148](https://github.com/BlockstreamResearch/SimplicityHL/pull/148)
[#149](https://github.com/BlockstreamResearch/SimplicityHL/pull/149)
* Switch NUMS key to BIP-0341 suggested key [#143](https://github.com/BlockstreamResearch/SimplicityHL/pull/143)
* Fix `array_fold` powers-of-two bug; fix simc CLI when serde is disabled; enable serde by default [#159](https://github.com/BlockstreamResearch/SimplicityHL/pull/159)
* Update rust-simplicity to 0.6
[#143](https://github.com/BlockstreamResearch/SimplicityHL/pull/143)
[#160](https://github.com/BlockstreamResearch/SimplicityHL/pull/160)

# 0.2.0 - 2025-07-29

* Renamed from [Simfony](https://crates.io/crates/simfony)
Expand Down
17 changes: 12 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "simplicityhl"
version = "0.2.0"
version = "0.3.0"
authors = ["sanket1729 <[email protected]>"]
license = "CC0-1.0"
homepage = "https://github.com/BlockstreamResearch/SimplicityHL"
Expand All @@ -27,7 +27,7 @@ pest = "2.1.3"
pest_derive = "2.7.1"
serde = { version = "1.0.188", features = ["derive"], optional = true }
serde_json = { version = "1.0.105", optional = true }
simplicity-lang = { version = "0.5.0" }
simplicity-lang = { version = "0.6.0" }
miniscript = "12.3.1"
either = "1.12.0"
itertools = "0.13.0"
Expand Down
19 changes: 11 additions & 8 deletions src/compile/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ use crate::named::CoreExt;
/// The fold `(fold f)_n : E^n × A → A`
/// takes the array of type `E^n` and an initial accumulator of type `A`,
/// and it produces the final accumulator of type `A`.
pub fn array_fold(size: NonZeroUsize, f: &ProgNode) -> Result<ProgNode, simplicity::types::Error> {
pub fn array_fold<'brand>(
size: NonZeroUsize,
f: &ProgNode<'brand>,
) -> Result<ProgNode<'brand>, simplicity::types::Error> {
/// Recursively fold the array using the precomputed folding functions.
fn tree_fold(
fn tree_fold<'brand>(
n: usize,
f_powers_of_two: &[ProgNode],
) -> Result<ProgNode, simplicity::types::Error> {
f_powers_of_two: &[ProgNode<'brand>],
) -> Result<ProgNode<'brand>, simplicity::types::Error> {
// Array is a left-balanced (right-associative) binary tree.
let max_pow2 = n.ilog2() as usize;
debug_assert!(max_pow2 < f_powers_of_two.len());
Expand All @@ -38,10 +41,10 @@ pub fn array_fold(size: NonZeroUsize, f: &ProgNode) -> Result<ProgNode, simplici
}

/// Fold the two arrays applying the folding function sequentially left -> right.
fn f_array_fold(
f_left: &ProgNode,
f_right: &ProgNode,
) -> Result<ProgNode, simplicity::types::Error> {
fn f_array_fold<'brand>(
f_left: &ProgNode<'brand>,
f_right: &ProgNode<'brand>,
) -> Result<ProgNode<'brand>, simplicity::types::Error> {
// The input is a tuple ((L, R), acc): ([E; n], A) where:
// - L and R are arrays of varying size E^x and E^y respectively (x + y = n).
// - acc is an accumulator of type A.
Expand Down
105 changes: 65 additions & 40 deletions src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::sync::Arc;
use either::Either;
use simplicity::jet::Elements;
use simplicity::node::{CoreConstructible as _, JetConstructible as _};
use simplicity::{Cmr, FailEntropy};
use simplicity::{types, Cmr, FailEntropy};

use self::builtins::array_fold;
use crate::array::{BTreeSlice, Partition};
Expand All @@ -26,7 +26,7 @@ use crate::value::StructuralValue;
use crate::witness::Arguments;
use crate::Value;

type ProgNode = Arc<named::ConstructNode<Elements>>;
type ProgNode<'brand> = Arc<named::ConstructNode<'brand, Elements>>;

/// Each SimplicityHL expression expects an _input value_.
/// A SimplicityHL expression is translated into a Simplicity expression
Expand All @@ -39,7 +39,7 @@ type ProgNode = Arc<named::ConstructNode<Elements>>;
/// Bindings from inner scopes overwrite bindings from outer scopes.
/// Bindings live as long as their scope.
#[derive(Debug, Clone)]
struct Scope {
struct Scope<'brand> {
/// For each scope, the set of assigned variables.
///
/// A stack of scopes. Each scope is a stack of patterns.
Expand All @@ -66,15 +66,15 @@ struct Scope {
/// Later assignments occur higher in the tree than earlier assignments.
/// ```
variables: Vec<Vec<Pattern>>,
ctx: simplicity::types::Context,
ctx: simplicity::types::Context<'brand>,
/// Tracker of function calls.
call_tracker: Arc<CallTracker>,
/// Values for parameters inside the SimplicityHL program.
arguments: Arguments,
include_debug_symbols: bool,
}

impl Scope {
impl<'brand> Scope<'brand> {
/// Create the main scope.
///
/// _This function should be called at the start of the compilation and then never again._
Expand All @@ -84,13 +84,14 @@ impl Scope {
/// The supplied `arguments` are consistent with the program's parameters.
/// Call [`Arguments::is_consistent`] before calling this method!
pub fn new(
ctx: simplicity::types::Context<'brand>,
call_tracker: Arc<CallTracker>,
arguments: Arguments,
include_debug_symbols: bool,
) -> Self {
Self {
variables: vec![vec![Pattern::Ignore]],
ctx: simplicity::types::Context::new(),
ctx,
call_tracker,
arguments,
include_debug_symbols,
Expand Down Expand Up @@ -183,12 +184,12 @@ impl Scope {
/// ```
///
/// The expression `drop (IOH & OH)` returns the seeked value.
pub fn get(&self, target: &BasePattern) -> Option<PairBuilder<ProgNode>> {
pub fn get(&self, target: &BasePattern) -> Option<PairBuilder<ProgNode<'brand>>> {
BasePattern::from(&self.get_input_pattern()).translate(&self.ctx, target)
}

/// Access the Simplicity type inference context.
pub fn ctx(&self) -> &simplicity::types::Context {
pub fn ctx(&self) -> &simplicity::types::Context<'brand> {
&self.ctx
}

Expand All @@ -200,10 +201,10 @@ impl Scope {
/// for debug symbols will simply ignore it. The semantics of the program remain unchanged.
pub fn with_debug_symbol<S: AsRef<Span>>(
&mut self,
args: PairBuilder<ProgNode>,
body: &ProgNode,
args: PairBuilder<ProgNode<'brand>>,
body: &ProgNode<'brand>,
span: &S,
) -> Result<PairBuilder<ProgNode>, RichError> {
) -> Result<PairBuilder<ProgNode<'brand>>, RichError> {
match self.call_tracker.get_cmr(span.as_ref()) {
Some(cmr) if self.include_debug_symbols => {
let false_and_args = ProgNode::bit(self.ctx(), false).pair(args);
Expand All @@ -221,12 +222,12 @@ impl Scope {
}
}

fn compile_blk(
fn compile_blk<'brand>(
stmts: &[Statement],
scope: &mut Scope,
scope: &mut Scope<'brand>,
index: usize,
last_expr: Option<&Expression>,
) -> Result<PairBuilder<ProgNode>, RichError> {
) -> Result<PairBuilder<ProgNode<'brand>>, RichError> {
if index >= stmts.len() {
return match last_expr {
Some(expr) => expr.compile(scope),
Expand Down Expand Up @@ -263,22 +264,28 @@ impl Program {
arguments: Arguments,
include_debug_symbols: bool,
) -> Result<Arc<named::CommitNode<Elements>>, RichError> {
let mut scope = Scope::new(
Arc::clone(self.call_tracker()),
arguments,
include_debug_symbols,
);

let main = self.main();
let construct = main.compile(&mut scope).map(PairBuilder::build)?;
// SimplicityHL types should be correct by construction. If not, assign the
// whole main function as the span for them, which is as sensible as anything.
named::finalize_types(&construct).with_span(main)
types::Context::with_context(|ctx| {
let mut scope = Scope::new(
ctx,
Arc::clone(self.call_tracker()),
arguments,
include_debug_symbols,
);

let main = self.main();
let construct = main.compile(&mut scope).map(PairBuilder::build)?;
// SimplicityHL types should be correct by construction. If not, assign the
// whole main function as the span for them, which is as sensible as anything.
named::finalize_types(&construct).with_span(main)
})
}
}

impl Expression {
fn compile(&self, scope: &mut Scope) -> Result<PairBuilder<ProgNode>, RichError> {
fn compile<'brand>(
&self,
scope: &mut Scope<'brand>,
) -> Result<PairBuilder<ProgNode<'brand>>, RichError> {
match self.inner() {
ExpressionInner::Block(stmts, expr) => {
scope.push_scope();
Expand All @@ -292,7 +299,10 @@ impl Expression {
}

impl SingleExpression {
fn compile(&self, scope: &mut Scope) -> Result<PairBuilder<ProgNode>, RichError> {
fn compile<'brand>(
&self,
scope: &mut Scope<'brand>,
) -> Result<PairBuilder<ProgNode<'brand>>, RichError> {
let expr = match self.inner() {
SingleExpressionInner::Constant(value) => {
let value = StructuralValue::from(value);
Expand Down Expand Up @@ -360,7 +370,10 @@ impl SingleExpression {
}

impl Call {
fn compile(&self, scope: &mut Scope) -> Result<PairBuilder<ProgNode>, RichError> {
fn compile<'brand>(
&self,
scope: &mut Scope<'brand>,
) -> Result<PairBuilder<ProgNode<'brand>>, RichError> {
let args_ast = SingleExpression::tuple(self.args().clone(), *self.as_ref());
let args = args_ast.compile(scope)?;

Expand Down Expand Up @@ -453,8 +466,13 @@ impl Call {
/// The fold `(fold f)_n : E^(<2^n) × A → A`
/// takes the list of type `E^(<2^n)` and an initial accumulator of type `A`,
/// and it produces the final accumulator of type `A`.
fn list_fold(bound: NonZeroPow2Usize, f: &ProgNode) -> Result<ProgNode, simplicity::types::Error> {
fn next_f_array(f_array: &ProgNode) -> Result<ProgNode, simplicity::types::Error> {
fn list_fold<'brand>(
bound: NonZeroPow2Usize,
f: &ProgNode<'brand>,
) -> Result<ProgNode<'brand>, simplicity::types::Error> {
fn next_f_array<'brand>(
f_array: &ProgNode<'brand>,
) -> Result<ProgNode<'brand>, simplicity::types::Error> {
/* f_(n + 1) : E^(2^(n + 1)) × A → A
* f_(n + 1) := OIH ▵ (OOH ▵ IH; f_n); f_n
*/
Expand All @@ -464,10 +482,10 @@ fn list_fold(bound: NonZeroPow2Usize, f: &ProgNode) -> Result<ProgNode, simplici
let half2_acc = ProgNode::o().i().h(ctx).pair(updated_acc);
half2_acc.comp(f_array).map(PairBuilder::build)
}
fn next_f_fold(
f_array: &ProgNode,
f_fold: &ProgNode,
) -> Result<ProgNode, simplicity::types::Error> {
fn next_f_fold<'brand>(
f_array: &ProgNode<'brand>,
f_fold: &ProgNode<'brand>,
) -> Result<ProgNode<'brand>, simplicity::types::Error> {
/* (fold f)_(n + 1) : E<2^(n + 1) × A → A
* (fold f)_(n + 1) := OOH ▵ (OIH ▵ IH);
* case (drop (fold f)_n)
Expand Down Expand Up @@ -525,16 +543,18 @@ fn list_fold(bound: NonZeroPow2Usize, f: &ProgNode) -> Result<ProgNode, simplici
/// In this case, the loop continues without returning anything.
/// The loop returns the final iterator after the final iteration
/// if `f` never returned a successful output.
fn for_while(
fn for_while<'brand>(
bit_width: Pow2Usize,
f: PairBuilder<ProgNode>,
) -> Result<PairBuilder<ProgNode>, simplicity::types::Error> {
f: PairBuilder<ProgNode<'brand>>,
) -> Result<PairBuilder<ProgNode<'brand>>, simplicity::types::Error> {
/* for_while_0 f : E × A → A
* for_while_0 f := (OH ▵ (IH ▵ false); f) ▵ IH;
* case (injl OH)
* (OH ▵ (IH ▵ true); f)
*/
fn for_while_0(f: &ProgNode) -> Result<PairBuilder<ProgNode>, simplicity::types::Error> {
fn for_while_0<'brand>(
f: &ProgNode<'brand>,
) -> Result<PairBuilder<ProgNode<'brand>>, simplicity::types::Error> {
let ctx = f.inference_context();
let f_output = ProgNode::o()
.h(ctx)
Expand All @@ -557,7 +577,9 @@ fn for_while(
* where
* f : A × (C × 2^(2^(n + 1))) → B + A
*/
fn adapt_f(f: &ProgNode) -> Result<PairBuilder<ProgNode>, simplicity::types::Error> {
fn adapt_f<'brand>(
f: &ProgNode<'brand>,
) -> Result<PairBuilder<ProgNode<'brand>>, simplicity::types::Error> {
let ctx = f.inference_context();
let f_input = ProgNode::o().h(ctx).pair(
ProgNode::i()
Expand Down Expand Up @@ -626,7 +648,10 @@ fn for_while(
}

impl Match {
fn compile(&self, scope: &mut Scope) -> Result<PairBuilder<ProgNode>, RichError> {
fn compile<'brand>(
&self,
scope: &mut Scope<'brand>,
) -> Result<PairBuilder<ProgNode<'brand>>, RichError> {
scope.push_scope();
scope.insert(
self.left()
Expand Down
Loading