Skip to content

Language Ports

Native ports of the Alap expression parser for Python, PHP, Go, and Rust. These enable server-side expression resolution without a Node.js sidecar.

Source: src/other-languages/

What's included

ModulePythonPHPGoRust
Expression parserexpression_parser.pyExpressionParser.phpalap.goalap-core crate
Regex validatorvalidate_regex.pyBuilt-inBuilt-inBuilt-in
URL sanitizersanitize_url.pyBuilt-inBuilt-inBuilt-in
Config validatorBuilt-inBuilt-inBuilt-invalidate_config.rs
SSRF guardssrf_guard.pyBuilt-inBuilt-inssrf_guard.rs
Config mergerBuilt-inBuilt-inBuilt-inBuilt-in

All ports support the full expression grammar including:

  • Item IDs, tags (.coffee), macros (@favorites)
  • Operators (+, |, -) with left-to-right evaluation
  • Parenthesized grouping (up to 32 levels)
  • Regex search (/pattern/fields)
  • Protocol expressions (:time:30d:, :loc:args:)
  • Refiners (*sort:label*, *limit:5*)

All ports share the \w identifier constraint — item IDs, macro names, and tag names cannot contain hyphens (the - character is the WITHOUT operator).

Security parity

All ports include the same security layers as the TypeScript implementation:

FeaturePythonPHPGoRust
URL sanitizationyesyesyesyes
Prototype-pollution defenseyes (+ dunders)yesyesyes
Resource limits (depth/tokens)yesyesyesyes
ReDoS detectionsyntacticsyntactic + pcre.backtrack_limitN/A (RE2)N/A (safe engine)
validateConfigyesyesyesyes
SSRF guardyesyesyesyes

Language-specific defenses:

  • Python: Blocks dunder keys (__class__, __bases__, __mro__, __subclasses__) in validate_config — prevents downstream exploits if configs are passed to Jinja2 or logging formatters.
  • PHP: Rejects non-array input to validateConfig() (enforces json_decode($json, true)). Wraps regex execution with pcre.backtrack_limit as a circuit breaker.
  • Go: SSRF guard handles IPv4-mapped IPv6 addresses (::ffff:127.0.0.1) via net.IP.To4().
  • Rust: SSRF guard blocks hex/octal/integer IP obfuscation (0x7f.0.0.1, 0177.0.0.1, 2130706433).

See Security for the full cross-language matrix.

What's NOT included

These are server-side ports. Browser-side concerns stay in the TypeScript client:

  • DOM rendering, compass-based menu placement, event handling
  • Viewport containment and placement fallback
  • CSS injection, alapelem container management

Python

bash
pip install alap
# or: uv add alap
python
from expression_parser import ExpressionParser, resolve_expression, cherry_pick_links, merge_configs

config = load_config_from_db()
parser = ExpressionParser(config)
ids = parser.query('.coffee + .nyc')
links = resolve_expression(config, '.coffee')

Tests: 50+ tests covering operands, operators, macros, parentheses, regex, protocols, refiners.

PHP

bash
composer require danielsmith/alap
php
use Alap\ExpressionParser;

$config = json_decode(file_get_contents('config.json'), true);
$parser = new ExpressionParser($config);
$ids = $parser->query('.coffee + .nyc');

Tests: PHPUnit suite with 50+ tests.

Go

go
import "github.com/DanielSmith/alap-go"

config := loadConfig()
parser := alap.NewExpressionParser(config)
ids := parser.Query(".coffee + .nyc")

Tests: 50+ tests via go test.

Rust

toml
[dependencies]
alap = "0.1"
rust
use alap::ExpressionParser;

let config = load_config();
let parser = ExpressionParser::new(&config);
let ids = parser.query(".coffee + .nyc");

Tests: Full test suite via cargo test. The Rust port uses edition 2024.

Used by the server examples

The language ports power expression resolution in the non-Node servers:

ServerParser
Flask, Django, FastAPIPython port
LaravelPHP port
GinGo port
AxumRust port
Express, Hono, BunTypeScript alap/core

See Servers for the full server matrix.