Add JAX typography, xector primitives, deferred effect chains, and GPU streaming
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m28s

- Add JAX text rendering with font atlas, styled text placement, and typography primitives
- Add xector (element-wise/reduction) operations library and sexp effects
- Add deferred effect chain fusion for JIT-compiled effect pipelines
- Expand drawing primitives with font management, alignment, shadow, and outline
- Add interpreter support for function-style define and require
- Add GPU persistence mode and hardware decode support to streaming
- Add new sexp effects: cell_pattern, halftone, mosaic, and derived definitions
- Add path registry for asset resolution
- Add integration, primitives, and xector tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-02-06 15:12:54 +00:00
parent dbc4ece2cc
commit fc9597456f
30 changed files with 7749 additions and 165 deletions

View File

@@ -156,11 +156,21 @@ class Interpreter:
if form == 'define':
name = expr[1]
if _is_symbol(name):
# Simple define: (define name value)
value = self.eval(expr[2], env)
self.global_env.set(name.name, value)
return value
elif isinstance(name, list) and len(name) >= 1 and _is_symbol(name[0]):
# Function define: (define (fn-name args...) body)
# Desugars to: (define fn-name (lambda (args...) body))
fn_name = name[0].name
params = [p.name if _is_symbol(p) else p for p in name[1:]]
body = expr[2]
fn = Lambda(params, body, env)
self.global_env.set(fn_name, fn)
return fn
else:
raise SyntaxError(f"define requires symbol, got {name}")
raise SyntaxError(f"define requires symbol or (name args...), got {name}")
# Define-effect
if form == 'define-effect':
@@ -276,6 +286,10 @@ class Interpreter:
if form == 'require-primitives':
return self._eval_require_primitives(expr, env)
# require - load .sexp file into current scope
if form == 'require':
return self._eval_require(expr, env)
# Function call
fn = self.eval(head, env)
args = [self.eval(arg, env) for arg in expr[1:]]
@@ -488,6 +502,61 @@ class Interpreter:
from .primitive_libs import load_primitive_library
return load_primitive_library(name, path)
def _eval_require(self, expr: Any, env: Environment) -> Any:
"""
Evaluate require: load a .sexp file and evaluate its definitions.
Syntax:
(require "derived") ; loads derived.sexp from sexp_effects/
(require "path/to/file.sexp") ; loads from explicit path
Definitions from the file are added to the current environment.
"""
for lib_expr in expr[1:]:
if _is_symbol(lib_expr):
lib_name = lib_expr.name
else:
lib_name = lib_expr
# Find the .sexp file
sexp_path = self._find_sexp_file(lib_name)
if sexp_path is None:
raise ValueError(f"Cannot find sexp file: {lib_name}")
# Parse and evaluate the file
content = parse_file(sexp_path)
# Evaluate all top-level expressions
if isinstance(content, list) and content and isinstance(content[0], list):
for e in content:
self.eval(e, env)
else:
self.eval(content, env)
return None
def _find_sexp_file(self, name: str) -> Optional[str]:
"""Find a .sexp file by name."""
# Try various locations
candidates = [
# Explicit path
name,
name + '.sexp',
# In sexp_effects directory
Path(__file__).parent / f'{name}.sexp',
Path(__file__).parent / name,
# In effects directory
Path(__file__).parent / 'effects' / f'{name}.sexp',
Path(__file__).parent / 'effects' / name,
]
for path in candidates:
p = Path(path) if not isinstance(path, Path) else path
if p.exists() and p.is_file():
return str(p)
return None
def _eval_ascii_fx_zone(self, expr: Any, env: Environment) -> Any:
"""
Evaluate ascii-fx-zone special form.
@@ -876,8 +945,8 @@ class Interpreter:
for pname, pdefault in effect.params.items():
value = params.get(pname)
if value is None:
# Evaluate default if it's an expression (list)
if isinstance(pdefault, list):
# Evaluate default if it's an expression (list) or a symbol (like 'nil')
if isinstance(pdefault, list) or _is_symbol(pdefault):
value = self.eval(pdefault, env)
else:
value = pdefault