Skip to main content

openscad_evaluator

An AST evaluator for the OpenSCAD language, producing Manifold CSG geometry from a parsed AST.

Tests PyPI version License: MIT

Overview

openscad_evaluator takes an OpenSCAD AST — as produced by openscad_lalr_parser — and walks it to produce Manifold meshes: a two-pass resolve (AST walk, no CSG calls) then generate (bottom-up Manifold/CrossSection construction) pipeline, with $fn/$fa/$fs, full built-in coverage (primitives, transforms, boolean ops, 2D geometry, text(), surface(), DXF/3MF import, roof()), and a content-hash geometry cache (ManifoldCache) so repeated renders/debugger pauses skip unchanged Manifold work.

It's GUI- and toolkit-agnostic: the only way it talks back to a caller is a handful of optional callback parameters on Evaluator.__init__ (echo_fn, debug_hook, error_break_fn, return_hook) — no QObject, no signals, no direct rendering. This is the evaluator that powers BelfrySCAD.

See docs/evaluator.md for the full architecture reference: scope processing, assignment order, the built-ins table, 2D/3D geometry handling, error format, $variables scoping, include/use, and the Manifold provenance / AST-to-geometry-ID mapping used for WYSIWYG picking.

Installation

pip install openscad-evaluator

For DXF import support:

pip install openscad-evaluator[dxf]

STL/OBJ/OFF/3MF export needs nothing extra -- all four writers are pure Python (export.py; 3MF is just a ZIP of XML, built with the standard library's zipfile/xml.etree.ElementTree, not the platform-limited lib3mf).

From Source

git clone https://github.com/BelfrySCAD/openscad_evaluator.git
cd openscad_evaluator
pip install -e ".[dev]"

Quick Start

from openscad_lalr_parser import getASTfromString, build_scopes
from openscad_evaluator import Evaluator

nodes = getASTfromString("cube([10, 10, 10]);")
root_scope = build_scopes(nodes)

ev = Evaluator()
bodies, id_to_node = ev.evaluate(nodes, root_scope)
for body in bodies:
    print(body.body.num_tri(), "triangles")

A body carries a 3D body (Manifold), a 2D section (CrossSection), or -- for an open mesh that doesn't close a solid, which OpenSCAD still draws -- neither, with the triangles in raw_mesh. A renderer or exporter should draw those too; export.py's writers do.

Language extensions

Shared with openscad_cpp_evaluator, and not part of upstream OpenSCAD:

obj = render() { difference() { cube(10, center=true); sphere(4); } };
echo(obj.volume, obj.area, obj.genus, obj.boundingbox);   // measured; nothing is drawn
polyhedron(obj);                                          // and straight back in
polyhedron(spheroid(d=30));                               // any BOSL2 VNF, too

render() in expression position builds its children's geometry, measures it and discards it. render is therefore a reserved word (openscad_lalr_parser >= 1.2.0), and the braced form is required. See docs/evaluator.md for the object's keys.

Command Line

Installing the package also installs an openscad-evaluator script that evaluates a .scad file and exports the result -- STL, OBJ, OFF, or 3MF, inferred from -o's extension:

openscad-evaluator model.scad -o model.stl
openscad-evaluator model.scad -o model.3mf
openscad-evaluator model.scad -o model.mesh --format obj   # explicit format overrides the extension

echo()/warning output goes to stdout; a parse or evaluation error goes to stderr and exits non-zero.

--debug drops into a small, gdb-style interactive debugger instead of running straight through: set breakpoints and run, then step/next/finish/continue, print <name>, backtrace, and set <name>=<value> to override a variable before resuming.

$ openscad-evaluator model.scad -o model.stl --debug
Reading symbols from model.scad...
(scad-dbg) break 12
Breakpoint set at model.scad:12
(scad-dbg) run

Breakpoint hit at model.scad:12
     10  module bracket(w) {
     11      h = w * 2;
->   12      cube([w, h, 3]);
     13  }
(scad-dbg) print h
$1 = 20
(scad-dbg) continue
Exported to model.stl

See src/openscad_evaluator/_debug_repl.py for the full command list (help at either prompt also prints it), and examples/minimal_debugger.py for the underlying debug_hook protocol if you're integrating your own debugger rather than using this one.

Examples

Runnable, self-checking scripts under examples/ go beyond the Quick Start above to cover the two optional integration points most callers ask about:

  • examples/minimal_debugger.py — hooking a debugger into Evaluator(debug_hook=...): tracing every statement, stopping evaluation at a breakpoint, and overriding a variable's value mid-run via the hook's mods return value.
  • examples/manifold_cache_reuse.py — sharing one ManifoldCache across repeated evaluate() calls so an unchanged subtree (e.g. everything but the part of the script a user just edited) skips re-running Manifold work on the next render.
python examples/minimal_debugger.py
python examples/manifold_cache_reuse.py

Both are also run under pytest (tests/test_examples.py) so they fail CI if they drift out of sync with the real API.

Development

uv sync --all-extras
uv run pytest

Release files for openscad-evaluator 1.3.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openscad-evaluator 1.3.3
File Size Uploaded
openscad_evaluator-1.3.3.tar.gz 298.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openscad-evaluator 1.3.3
File Interpreter ABI Platform
openscad_evaluator-1.3.3-py3-none-any.whl Python 3 none any Details

Total release size: 599.8 kB

Release files / openscad_evaluator-1.3.3.tar.gz

Download URL openscad_evaluator-1.3.3.tar.gz
Size 298.6 kB
Tags Source
SHA-256 checksum
How to use checksums
46277dacbd93f9ddcc9a58adca9a53cc004ca858b2e7e8d503973bbc7a884c86
BLAKE2b-256 checksum
How to use checksums
99768f7cc88a6e044f212c109d346571f42d8a5384418dd99c32e4e2418cce22
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / openscad_evaluator-1.3.3-py3-none-any.whl

Download URL openscad_evaluator-1.3.3-py3-none-any.whl
Size 301.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3a905830d4b8bf26fe338c2be03ddc754ae655e7378217907b6b8fa2802b7551
BLAKE2b-256 checksum
How to use checksums
fa8f04c0eb28774adac3c2326f5b59b79fabc31b268443cd66fe0fbce58fb138
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

1.4.2

2 release files

1.4.1

2 release files

1.4.0

2 release files

This release

1.3.3 This release

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page