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
| Module | Python | PHP | Go | Rust |
|---|---|---|---|---|
| Expression parser | expression_parser.py | ExpressionParser.php | alap.go | alap-core crate |
| Regex validator | validate_regex.py | Built-in | Built-in | Built-in |
| URL sanitizer | sanitize_url.py | Built-in | Built-in | Built-in |
| Config validator | Built-in | Built-in | Built-in | validate_config.rs |
| SSRF guard | ssrf_guard.py | Built-in | Built-in | ssrf_guard.rs |
| Config merger | Built-in | Built-in | Built-in | Built-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:
| Feature | Python | PHP | Go | Rust |
|---|---|---|---|---|
| URL sanitization | yes | yes | yes | yes |
| Prototype-pollution defense | yes (+ dunders) | yes | yes | yes |
| Resource limits (depth/tokens) | yes | yes | yes | yes |
| ReDoS detection | syntactic | syntactic + pcre.backtrack_limit | N/A (RE2) | N/A (safe engine) |
validateConfig | yes | yes | yes | yes |
| SSRF guard | yes | yes | yes | yes |
Language-specific defenses:
- Python: Blocks dunder keys (
__class__,__bases__,__mro__,__subclasses__) invalidate_config— prevents downstream exploits if configs are passed to Jinja2 or logging formatters. - PHP: Rejects non-array input to
validateConfig()(enforcesjson_decode($json, true)). Wraps regex execution withpcre.backtrack_limitas a circuit breaker. - Go: SSRF guard handles IPv4-mapped IPv6 addresses (
::ffff:127.0.0.1) vianet.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,
alapelemcontainer management
Python
pip install alap
# or: uv add alapfrom 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
composer require danielsmith/alapuse 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
import "github.com/DanielSmith/alap-go"
config := loadConfig()
parser := alap.NewExpressionParser(config)
ids := parser.Query(".coffee + .nyc")Tests: 50+ tests via go test.
Rust
[dependencies]
alap = "0.1"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:
| Server | Parser |
|---|---|
| Flask, Django, FastAPI | Python port |
| Laravel | PHP port |
| Gin | Go port |
| Axum | Rust port |
| Express, Hono, Bun | TypeScript alap/core |
See Servers for the full server matrix.