Skip to content

feat: script builder#163

Merged
ascandone merged 11 commits into
mainfrom
feat/script-builder
Jun 23, 2026
Merged

feat: script builder#163
ascandone merged 11 commits into
mainfrom
feat/script-builder

Conversation

@ascandone

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Introduces a new builder package for programmatically constructing numscript programs. The package provides a typed Go DSL with generic expression types, pool-based variable name allocation, source/destination/statement renderers, and a BuildProgram entry point that emits a vars block and statement body. Seven tests validate all rendering paths.

Changes

Numscript builder DSL

Layer / File(s) Summary
Core infrastructure: ExprType interfaces, pool, env, render type, and name helpers
builder/expression_type.go, builder/pool.go, builder/builder.go
Defines exported ExprType marker interfaces, unexported generic pool[T] with monotonic ID allocation, the env struct holding typed pools and a shared strings.Builder, the render function type, indentation helpers, and deterministic account_<id>/asset_<id>/string_<id>/number_<id> name converters.
Variable system: Var type, constructors, and Fill methods
builder/vars.go
Introduces generic Var[T] with a per-type alloc closure, type-erased bindings map in VarsEnv, exports NewAccountVar, NewAssetVar, NewStringVar, NewNumberVar constructors, and Fill* methods returning (varName, valueString) pairs.
Expression constructors
builder/expression.go
Exports Expression[T] renderer type and pool-backed constructors (ExprVar, ExprAccount, ExprAsset, ExprString, ExprNumberBigInt, ExprMonetary) that resolve names through pools and write $name or [asset amount] representations into the shared builder.
Statement DSL: Source, Overdraft, Destination, Statement constructors
builder/source.go, builder/destination.go, builder/statement.go
Defines Source, Overdraft, Destination, and Statement render-function aliases; exports UnboundedOverdraft, BoundedOverdraft, SrcAccount, SrcAccountOverdraft, SrcColored, SrcColoredOverdraft, SrcInorder, DestAccount, and StmtSend with indented rendering.
Vars block rendering and BuildProgram entry point
builder/builder.go
Implements varRenderState, renderVar, and renderVars to emit the vars { ... } section, populate knownBindings, and conditionally suppress the block when empty; exports BuildProgram returning (knownBindings, varsEnv, script).
Tests
builder/builder_test.go
Adds seven tests (TestSimpleSend, TestSrcAllowingBoundedOverdraft, TestColoredAllowingUnboundedOverdraft, TestInorder, TestInorderNested, TestInorderWithColors, TestWithExternVar) validating inline snapshots, vars binding maps, and typed Fill* returns.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant BuildProgram
  participant env
  participant StmtSend
  participant renderVars

  Caller->>BuildProgram: BuildProgram(StmtSend(monetary, source, dest))
  BuildProgram->>env: newEnv() — init pools, varsEnv, builder
  BuildProgram->>StmtSend: render(env, indentLevel=0)
  StmtSend->>env: WriteString "send [asset amount]"
  StmtSend->>env: WriteString "(source = ..."
  StmtSend->>env: render Source (SrcAccount / SrcInorder / SrcColored)
  StmtSend->>env: render Destination (DestAccount)
  StmtSend->>env: WriteString ")"
  BuildProgram->>renderVars: renderVars(env, accountsPool, assetsPool, ...)
  renderVars->>renderVars: iterate pooled IDs → emit "account $account_0 = ..."
  renderVars-->>BuildProgram: vars block string
  BuildProgram-->>Caller: (knownBindings, varsEnv, vars+body)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 A brand-new builder hops into the code,
Pooling variables down numscript road.
Accounts and assets get names on the fly,
vars { } blocks rendered — oh my, oh my!
BuildProgram ties it all into one string,
What a delightful new feature to bring! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to verify if the description relates to the changeset. Add a pull request description explaining the purpose, scope, and implementation details of the script builder feature.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: script builder' directly describes the main change - introducing a new script builder package with multiple files and comprehensive functionality for constructing formatted programs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/script-builder

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.59794% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.48%. Comparing base (cbc11e8) to head (b937f95).

Files with missing lines Patch % Lines
builder/vars.go 48.64% 19 Missing ⚠️
builder/builder.go 89.55% 4 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #163      +/-   ##
==========================================
+ Coverage   67.81%   68.48%   +0.67%     
==========================================
  Files          49       56       +7     
  Lines        5223     5417     +194     
==========================================
+ Hits         3542     3710     +168     
- Misses       1472     1495      +23     
- Partials      209      212       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Changes requested — automated review

The new builder can emit invalid scripts when builder values are reused, and its typed variable API does not enforce the advertised variable type at compile time. These are correctness issues in the newly introduced public API.

Comment thread builder/expression.go Outdated
Comment thread builder/vars.go Outdated
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approve — automated review

No discrete correctness issues were identified in the introduced builder implementation based on the diff and repository context.

No findings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@builder/vars.go`:
- Around line 57-59: The FillAccount method and similar methods (at lines 62-64,
67-69, 72-74) silently ignore failed variable lookups by discarding the success
boolean returned from the map access operation using a blank identifier. Instead
of ignoring the second return value from v.bindings lookups, capture it and
validate that the variable binding exists, returning an error or failing fast if
the variable is not found in the bindings map. Apply this validation pattern to
all affected methods to ensure missing variable bindings are caught immediately
rather than propagating invalid empty string names downstream.
- Around line 72-74: The FillNumber function does not check if the bi parameter
is nil before calling bi.String() on line 74, which will cause a panic if bi is
nil. Add an explicit nil check for the bi parameter before calling bi.String(),
and return an appropriate string representation (such as empty string or "nil")
when bi is nil to prevent the panic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cd1c5ee4-c3ef-4a1e-8998-0fd9a9e65bf0

📥 Commits

Reviewing files that changed from the base of the PR and between 39ba793 and ba27dbe.

📒 Files selected for processing (4)
  • builder/builder.go
  • builder/builder_test.go
  • builder/expression.go
  • builder/vars.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • builder/expression.go
  • builder/builder_test.go

Comment thread builder/vars.go
Comment on lines +57 to +59
func (v VarsEnv) FillAccount(var_ *Var[ExprTypeAccount], account string) (string, string) {
name, _ := v.bindings[anyVar(var_)]
return name, account

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast when a variable is not bound in VarsEnv.

At Line 58/63/68/73, missing keys are silently converted into "" names. That can emit invalid bindings downstream without any explicit failure signal. Validate lookup success and fail fast.

Suggested fix
 func (v VarsEnv) FillAccount(var_ *Var[ExprTypeAccount], account string) (string, string) {
-	name, _ := v.bindings[anyVar(var_)]
+	name, ok := v.bindings[anyVar(var_)]
+	if !ok {
+		panic("FillAccount called with variable not allocated in program")
+	}
 	return name, account
 }

 func (v VarsEnv) FillAsset(var_ *Var[ExprTypeAsset], asset string) (string, string) {
-	name, _ := v.bindings[anyVar(var_)]
+	name, ok := v.bindings[anyVar(var_)]
+	if !ok {
+		panic("FillAsset called with variable not allocated in program")
+	}
 	return name, asset
 }

 func (v VarsEnv) FillString(var_ *Var[ExprTypeString], str string) (string, string) {
-	name, _ := v.bindings[anyVar(var_)]
+	name, ok := v.bindings[anyVar(var_)]
+	if !ok {
+		panic("FillString called with variable not allocated in program")
+	}
 	return name, str
 }

 func (v VarsEnv) FillNumber(var_ *Var[ExprTypeNumber], bi *big.Int) (string, string) {
-	name, _ := v.bindings[anyVar(var_)]
+	name, ok := v.bindings[anyVar(var_)]
+	if !ok {
+		panic("FillNumber called with variable not allocated in program")
+	}
 	return name, bi.String()
 }

Also applies to: 62-64, 67-69, 72-74

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@builder/vars.go` around lines 57 - 59, The FillAccount method and similar
methods (at lines 62-64, 67-69, 72-74) silently ignore failed variable lookups
by discarding the success boolean returned from the map access operation using a
blank identifier. Instead of ignoring the second return value from v.bindings
lookups, capture it and validate that the variable binding exists, returning an
error or failing fast if the variable is not found in the bindings map. Apply
this validation pattern to all affected methods to ensure missing variable
bindings are caught immediately rather than propagating invalid empty string
names downstream.

Comment thread builder/vars.go
Comment on lines +72 to +74
func (v VarsEnv) FillNumber(var_ *Var[ExprTypeNumber], bi *big.Int) (string, string) {
name, _ := v.bindings[anyVar(var_)]
return name, bi.String()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard FillNumber against nil *big.Int.

At Line 74, bi.String() will panic when bi == nil. Add an explicit nil check before conversion.

Suggested fix
 func (v VarsEnv) FillNumber(var_ *Var[ExprTypeNumber], bi *big.Int) (string, string) {
 	name, _ := v.bindings[anyVar(var_)]
+	if bi == nil {
+		panic("FillNumber called with nil *big.Int")
+	}
 	return name, bi.String()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (v VarsEnv) FillNumber(var_ *Var[ExprTypeNumber], bi *big.Int) (string, string) {
name, _ := v.bindings[anyVar(var_)]
return name, bi.String()
func (v VarsEnv) FillNumber(var_ *Var[ExprTypeNumber], bi *big.Int) (string, string) {
name, _ := v.bindings[anyVar(var_)]
if bi == nil {
panic("FillNumber called with nil *big.Int")
}
return name, bi.String()
}
🧰 Tools
🪛 GitHub Actions: Default / Dirty

[error] 55-74: CI failed because the repository had uncommitted changes. The step 'git status --porcelain' detected modifications and exited with code 1 after showing 'There are changes in the repository' and running 'git diff' for modified file 'builder/vars.go'. Diff indicates changes to variable binding assignments in FillAccount/FillAsset/FillString/FillNumber.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@builder/vars.go` around lines 72 - 74, The FillNumber function does not check
if the bi parameter is nil before calling bi.String() on line 74, which will
cause a panic if bi is nil. Add an explicit nil check for the bi parameter
before calling bi.String(), and return an appropriate string representation
(such as empty string or "nil") when bi is nil to prevent the panic.

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approve — automated review

No discrete correctness issues were identified in the added builder package based on the diff and surrounding parser/runtime expectations.

No findings.

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approve — automated review

No discrete correctness issues were found in the added builder implementation based on the diff and existing Numscript grammar/API expectations.

No findings.

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Changes requested — automated review

The new builder can generate invalid Numscript for a typed API call by applying the unbounded overdraft clause to source forms where the grammar does not allow it.

Comment thread builder/source.go Outdated

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approve — automated review

I did not identify any discrete correctness issues in the added builder API from the diff. The generated scripts and variable binding approach appear consistent with the existing Numscript grammar and runtime expectations.

No findings.

Azorlogh
Azorlogh previously approved these changes Jun 23, 2026
@ascandone ascandone merged commit 9ca635b into main Jun 23, 2026
7 checks passed
@ascandone ascandone deleted the feat/script-builder branch June 23, 2026 09:51

@NumaryBot NumaryBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Changes requested — automated review

The builder can generate colored-source scripts that are rejected by the default interpreter execution path because the required feature flag is not included or surfaced.

Comment thread builder/source.go
) Source {
return func(env *env, w int) {
accountExpr(env, w)
env.builder.WriteString(" \\ ")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [major] Enable asset-colors when rendering colored sources

When callers use SrcColored/SrcColoredOverdraft and execute the returned script via the normal Parse(...).Run path, the interpreter rejects it because colored sources require the experimental-asset-colors feature flag, but BuildProgram never emits a #![feature(...)] declaration or returns required flags. The builder should track this feature or otherwise expose a way to make generated colored scripts runnable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants