Spec URL evaluation in router.sx, bootstrap to Python/JS
Add url-to-expr, auto-quote-unknowns, prepare-url-expr to router.sx — the canonical URL-to-expression pipeline. Dots→spaces, parse, then auto-quote unknown symbols as strings (slugs). The same spec serves both server (Python) and client (JS) route handling. - router.sx: three new pure functions for URL evaluation - bootstrap_py.py: auto-include router module with html adapter - platform_js.py: export urlToExpr/autoQuoteUnknowns/prepareUrlExpr - sx_router.py: replace hand-written auto_quote_slugs with bootstrapped prepare_url_expr — delete ~50 lines of hardcoded function name sets - Rebootstrap sx_ref.py (4331 lines) and sx-browser.js Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1254,7 +1254,8 @@ def compile_ref_to_py(
|
||||
if sm not in SPEC_MODULES:
|
||||
raise ValueError(f"Unknown spec module: {sm!r}. Valid: {', '.join(SPEC_MODULES)}")
|
||||
spec_mod_set.add(sm)
|
||||
# html adapter needs deps (component analysis) and signals (island rendering)
|
||||
# html adapter needs deps (component analysis), signals (island rendering),
|
||||
# router (URL-to-expression evaluation), and page-helpers
|
||||
if "html" in adapter_set:
|
||||
if "deps" in SPEC_MODULES:
|
||||
spec_mod_set.add("deps")
|
||||
@@ -1262,6 +1263,8 @@ def compile_ref_to_py(
|
||||
spec_mod_set.add("signals")
|
||||
if "page-helpers" in SPEC_MODULES:
|
||||
spec_mod_set.add("page-helpers")
|
||||
if "router" in SPEC_MODULES:
|
||||
spec_mod_set.add("router")
|
||||
has_deps = "deps" in spec_mod_set
|
||||
|
||||
# Core files always included, then selected adapters, then spec modules
|
||||
|
||||
@@ -3129,6 +3129,9 @@ def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has
|
||||
api_lines.append(' parseRoutePattern: parseRoutePattern,')
|
||||
api_lines.append(' matchRoute: matchRoute,')
|
||||
api_lines.append(' findMatchingRoute: findMatchingRoute,')
|
||||
api_lines.append(' urlToExpr: urlToExpr,')
|
||||
api_lines.append(' autoQuoteUnknowns: autoQuoteUnknowns,')
|
||||
api_lines.append(' prepareUrlExpr: prepareUrlExpr,')
|
||||
|
||||
if has_dom:
|
||||
api_lines.append(' registerIo: typeof registerIoPrimitive === "function" ? registerIoPrimitive : null,')
|
||||
|
||||
@@ -570,11 +570,111 @@
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Platform interface — none required
|
||||
;; 12. URL expression evaluation
|
||||
;; --------------------------------------------------------------------------
|
||||
;; All functions use only pure primitives:
|
||||
;; A URL is an expression. The system is the environment.
|
||||
;; eval(url, env) — that's it.
|
||||
;;
|
||||
;; The only URL-specific pre-processing:
|
||||
;; 1. Surface syntax → AST (dots to spaces, parse as SX)
|
||||
;; 2. Auto-quote unknowns (symbols not in env become strings)
|
||||
;;
|
||||
;; After that, it's standard eval. The host wires these into its route
|
||||
;; handlers (Python catch-all, JS client-side navigation). The same
|
||||
;; functions serve both.
|
||||
|
||||
(define url-to-expr :effects []
|
||||
(fn ((url-path :as string))
|
||||
;; Convert a URL path to an SX expression (AST).
|
||||
;;
|
||||
;; "/sx/(language.(doc.introduction))" → (language (doc introduction))
|
||||
;; "/(language.(doc.introduction))" → (language (doc introduction))
|
||||
;; "/" → (list) ; empty — home
|
||||
;;
|
||||
;; Steps:
|
||||
;; 1. Strip URL prefix ("/sx/" or "/") — host passes the path after prefix
|
||||
;; 2. Dots → spaces (URL-safe whitespace encoding)
|
||||
;; 3. Parse as SX expression
|
||||
;;
|
||||
;; The caller is responsible for stripping any app-level prefix.
|
||||
;; This function receives the raw expression portion: "(language.(doc.intro))"
|
||||
;; or "/" for home.
|
||||
(if (or (= url-path "/") (empty? url-path))
|
||||
(list)
|
||||
(let ((trimmed (if (starts-with? url-path "/")
|
||||
(slice url-path 1)
|
||||
url-path)))
|
||||
;; Dots → spaces
|
||||
(let ((sx-source (replace trimmed "." " ")))
|
||||
;; Parse — returns list of expressions, take the first
|
||||
(let ((exprs (sx-parse sx-source)))
|
||||
(if (empty? exprs)
|
||||
(list)
|
||||
(first exprs))))))))
|
||||
|
||||
|
||||
(define auto-quote-unknowns :effects []
|
||||
(fn ((expr :as list) (env :as dict))
|
||||
;; Walk an AST and replace symbols not in env with their name as a string.
|
||||
;; This makes URL slugs work without quoting:
|
||||
;; (language (doc introduction)) ; introduction is not a function
|
||||
;; → (language (doc "introduction"))
|
||||
;;
|
||||
;; Rules:
|
||||
;; - List head (call position) stays as-is — it's a function name
|
||||
;; - Tail symbols: if in env, keep as symbol; otherwise, string
|
||||
;; - Keywords, strings, numbers, nested lists: pass through
|
||||
;; - Non-list expressions: pass through unchanged
|
||||
(if (not (list? expr))
|
||||
expr
|
||||
(if (empty? expr)
|
||||
expr
|
||||
;; Head stays as symbol (function position), quote the rest
|
||||
(cons (first expr)
|
||||
(map (fn (child)
|
||||
(cond
|
||||
;; Nested list — recurse
|
||||
(list? child)
|
||||
(auto-quote-unknowns child env)
|
||||
;; Symbol — check env
|
||||
(= (type-of child) "symbol")
|
||||
(let ((name (symbol-name child)))
|
||||
(if (or (env-has? env name)
|
||||
;; Keep keywords, component refs, special forms
|
||||
(starts-with? name ":")
|
||||
(starts-with? name "~")
|
||||
(starts-with? name "!"))
|
||||
child
|
||||
name)) ;; unknown → string
|
||||
;; Everything else passes through
|
||||
:else child))
|
||||
(rest expr)))))))
|
||||
|
||||
|
||||
(define prepare-url-expr :effects []
|
||||
(fn ((url-path :as string) (env :as dict))
|
||||
;; Full pipeline: URL path → ready-to-eval AST.
|
||||
;;
|
||||
;; "(language.(doc.introduction))" + env
|
||||
;; → (language (doc "introduction"))
|
||||
;;
|
||||
;; The result can be fed directly to eval:
|
||||
;; (eval (prepare-url-expr path env) env)
|
||||
(let ((expr (url-to-expr url-path)))
|
||||
(if (empty? expr)
|
||||
expr
|
||||
(auto-quote-unknowns expr env)))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Platform interface
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Pure primitives used:
|
||||
;; split, slice, starts-with?, ends-with?, len, empty?, replace,
|
||||
;; map, filter, for-each, for-each-indexed, nth, get, dict-set!, merge,
|
||||
;; list, nil?, not, =, case, join, str, index-of, and, or, cons,
|
||||
;; first, rest, append, parse-int, contains?, min, cond
|
||||
;; first, rest, append, parse-int, contains?, min, cond,
|
||||
;; symbol?, symbol-name, list?, env-has?, type-of
|
||||
;;
|
||||
;; From parser.sx: sx-parse, sx-serialize
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
@@ -2881,6 +2881,361 @@ def build_affinity_analysis(demo_components, page_plans):
|
||||
return {'components': demo_components, 'page-plans': page_plans}
|
||||
|
||||
|
||||
# === Transpiled from router (client-side route matching) ===
|
||||
|
||||
# split-path-segments
|
||||
def split_path_segments(path):
|
||||
trimmed = (slice(path, 1) if sx_truthy(starts_with_p(path, '/')) else path)
|
||||
trimmed2 = (slice(trimmed, 0, (len(trimmed) - 1)) if sx_truthy(((not sx_truthy(empty_p(trimmed))) if not sx_truthy((not sx_truthy(empty_p(trimmed)))) else ends_with_p(trimmed, '/'))) else trimmed)
|
||||
if sx_truthy(empty_p(trimmed2)):
|
||||
return []
|
||||
else:
|
||||
return split(trimmed2, '/')
|
||||
|
||||
# make-route-segment
|
||||
def make_route_segment(seg):
|
||||
if sx_truthy((starts_with_p(seg, '<') if not sx_truthy(starts_with_p(seg, '<')) else ends_with_p(seg, '>'))):
|
||||
param_name = slice(seg, 1, (len(seg) - 1))
|
||||
d = {}
|
||||
d['type'] = 'param'
|
||||
d['value'] = param_name
|
||||
return d
|
||||
else:
|
||||
d = {}
|
||||
d['type'] = 'literal'
|
||||
d['value'] = seg
|
||||
return d
|
||||
|
||||
# parse-route-pattern
|
||||
def parse_route_pattern(pattern):
|
||||
segments = split_path_segments(pattern)
|
||||
return map(make_route_segment, segments)
|
||||
|
||||
# match-route-segments
|
||||
def match_route_segments(path_segs, parsed_segs):
|
||||
_cells = {}
|
||||
if sx_truthy((not sx_truthy((len(path_segs) == len(parsed_segs))))):
|
||||
return NIL
|
||||
else:
|
||||
params = {}
|
||||
_cells['matched'] = True
|
||||
for_each_indexed(lambda i, parsed_seg: ((lambda path_seg: (lambda seg_type: ((_sx_cell_set(_cells, 'matched', False) if sx_truthy((not sx_truthy((path_seg == get(parsed_seg, 'value'))))) else NIL) if sx_truthy((seg_type == 'literal')) else (_sx_dict_set(params, get(parsed_seg, 'value'), path_seg) if sx_truthy((seg_type == 'param')) else _sx_cell_set(_cells, 'matched', False))))(get(parsed_seg, 'type')))(nth(path_segs, i)) if sx_truthy(_cells['matched']) else NIL), parsed_segs)
|
||||
if sx_truthy(_cells['matched']):
|
||||
return params
|
||||
else:
|
||||
return NIL
|
||||
|
||||
# match-route
|
||||
def match_route(path, pattern):
|
||||
path_segs = split_path_segments(path)
|
||||
parsed_segs = parse_route_pattern(pattern)
|
||||
return match_route_segments(path_segs, parsed_segs)
|
||||
|
||||
# find-matching-route
|
||||
def find_matching_route(path, routes):
|
||||
_cells = {}
|
||||
match_path = ((sx_url_to_path(path) if sx_truthy(sx_url_to_path(path)) else path) if sx_truthy(starts_with_p(path, '/(')) else path)
|
||||
path_segs = split_path_segments(match_path)
|
||||
_cells['result'] = NIL
|
||||
for route in routes:
|
||||
if sx_truthy(is_nil(_cells['result'])):
|
||||
params = match_route_segments(path_segs, get(route, 'parsed'))
|
||||
if sx_truthy((not sx_truthy(is_nil(params)))):
|
||||
matched = merge(route, {})
|
||||
matched['params'] = params
|
||||
_cells['result'] = matched
|
||||
return _cells['result']
|
||||
|
||||
# _fn-to-segment
|
||||
def _fn_to_segment(name):
|
||||
_match = name
|
||||
if _match == 'doc':
|
||||
return 'docs'
|
||||
elif _match == 'spec':
|
||||
return 'specs'
|
||||
elif _match == 'bootstrapper':
|
||||
return 'bootstrappers'
|
||||
elif _match == 'test':
|
||||
return 'testing'
|
||||
elif _match == 'example':
|
||||
return 'examples'
|
||||
elif _match == 'protocol':
|
||||
return 'protocols'
|
||||
elif _match == 'essay':
|
||||
return 'essays'
|
||||
elif _match == 'plan':
|
||||
return 'plans'
|
||||
elif _match == 'reference-detail':
|
||||
return 'reference'
|
||||
else:
|
||||
return name
|
||||
|
||||
# sx-url-to-path
|
||||
def sx_url_to_path(url):
|
||||
if sx_truthy((not sx_truthy((starts_with_p(url, '/(') if not sx_truthy(starts_with_p(url, '/(')) else ends_with_p(url, ')'))))):
|
||||
return NIL
|
||||
else:
|
||||
inner = slice(url, 2, (len(url) - 1))
|
||||
s = replace(replace(replace(inner, '.', '/'), '(', ''), ')', '')
|
||||
segs = filter(lambda s: (not sx_truthy(empty_p(s))), split(s, '/'))
|
||||
return sx_str('/', join('/', map(_fn_to_segment, segs)))
|
||||
|
||||
# _count-leading-dots
|
||||
def _count_leading_dots(s):
|
||||
if sx_truthy(empty_p(s)):
|
||||
return 0
|
||||
else:
|
||||
if sx_truthy(starts_with_p(s, '.')):
|
||||
return (1 + _count_leading_dots(slice(s, 1)))
|
||||
else:
|
||||
return 0
|
||||
|
||||
# _strip-trailing-close
|
||||
def _strip_trailing_close(s):
|
||||
if sx_truthy(ends_with_p(s, ')')):
|
||||
return _strip_trailing_close(slice(s, 0, (len(s) - 1)))
|
||||
else:
|
||||
return s
|
||||
|
||||
# _index-of-safe
|
||||
def _index_of_safe(s, needle):
|
||||
idx = index_of(s, needle)
|
||||
if sx_truthy((is_nil(idx) if sx_truthy(is_nil(idx)) else (idx < 0))):
|
||||
return NIL
|
||||
else:
|
||||
return idx
|
||||
|
||||
# _last-index-of
|
||||
def _last_index_of(s, needle):
|
||||
idx = _index_of_safe(s, needle)
|
||||
if sx_truthy(is_nil(idx)):
|
||||
return NIL
|
||||
else:
|
||||
rest_idx = _last_index_of(slice(s, (idx + 1)), needle)
|
||||
if sx_truthy(is_nil(rest_idx)):
|
||||
return idx
|
||||
else:
|
||||
return ((idx + 1) + rest_idx)
|
||||
|
||||
# _pop-sx-url-level
|
||||
def _pop_sx_url_level(url):
|
||||
stripped = _strip_trailing_close(url)
|
||||
close_count = (len(url) - len(_strip_trailing_close(url)))
|
||||
if sx_truthy((close_count <= 1)):
|
||||
return '/'
|
||||
else:
|
||||
last_dp = _last_index_of(stripped, '.(')
|
||||
if sx_truthy(is_nil(last_dp)):
|
||||
return '/'
|
||||
else:
|
||||
return sx_str(slice(stripped, 0, last_dp), slice(url, (len(url) - (close_count - 1))))
|
||||
|
||||
# _pop-sx-url-levels
|
||||
def _pop_sx_url_levels(url, n):
|
||||
if sx_truthy((n <= 0)):
|
||||
return url
|
||||
else:
|
||||
return _pop_sx_url_levels(_pop_sx_url_level(url), (n - 1))
|
||||
|
||||
# _split-pos-kw
|
||||
def _split_pos_kw(tokens, i, pos, kw):
|
||||
if sx_truthy((i >= len(tokens))):
|
||||
return {'positional': join('.', pos), 'keywords': kw}
|
||||
else:
|
||||
tok = nth(tokens, i)
|
||||
if sx_truthy(starts_with_p(tok, ':')):
|
||||
val = (nth(tokens, (i + 1)) if sx_truthy(((i + 1) < len(tokens))) else '')
|
||||
return _split_pos_kw(tokens, (i + 2), pos, append(kw, [[tok, val]]))
|
||||
else:
|
||||
return _split_pos_kw(tokens, (i + 1), append(pos, [tok]), kw)
|
||||
|
||||
# _parse-relative-body
|
||||
def _parse_relative_body(body):
|
||||
if sx_truthy(empty_p(body)):
|
||||
return {'positional': '', 'keywords': []}
|
||||
else:
|
||||
return _split_pos_kw(split(body, '.'), 0, [], [])
|
||||
|
||||
# _extract-innermost
|
||||
def _extract_innermost(url):
|
||||
stripped = _strip_trailing_close(url)
|
||||
suffix = slice(url, len(_strip_trailing_close(url)))
|
||||
last_dp = _last_index_of(stripped, '.(')
|
||||
if sx_truthy(is_nil(last_dp)):
|
||||
return {'before': '/(', 'content': slice(stripped, 2), 'suffix': suffix}
|
||||
else:
|
||||
return {'before': slice(stripped, 0, (last_dp + 2)), 'content': slice(stripped, (last_dp + 2)), 'suffix': suffix}
|
||||
|
||||
# _find-kw-in-tokens
|
||||
def _find_kw_in_tokens(tokens, i, kw):
|
||||
if sx_truthy((i >= len(tokens))):
|
||||
return NIL
|
||||
else:
|
||||
if sx_truthy(((nth(tokens, i) == kw) if not sx_truthy((nth(tokens, i) == kw)) else ((i + 1) < len(tokens)))):
|
||||
return nth(tokens, (i + 1))
|
||||
else:
|
||||
return _find_kw_in_tokens(tokens, (i + 1), kw)
|
||||
|
||||
# _find-keyword-value
|
||||
def _find_keyword_value(content, kw):
|
||||
return _find_kw_in_tokens(split(content, '.'), 0, kw)
|
||||
|
||||
# _replace-kw-in-tokens
|
||||
def _replace_kw_in_tokens(tokens, i, kw, value):
|
||||
if sx_truthy((i >= len(tokens))):
|
||||
return []
|
||||
else:
|
||||
if sx_truthy(((nth(tokens, i) == kw) if not sx_truthy((nth(tokens, i) == kw)) else ((i + 1) < len(tokens)))):
|
||||
return append([kw, value], _replace_kw_in_tokens(tokens, (i + 2), kw, value))
|
||||
else:
|
||||
return cons(nth(tokens, i), _replace_kw_in_tokens(tokens, (i + 1), kw, value))
|
||||
|
||||
# _set-keyword-in-content
|
||||
def _set_keyword_in_content(content, kw, value):
|
||||
current = _find_keyword_value(content, kw)
|
||||
if sx_truthy(is_nil(current)):
|
||||
return sx_str(content, '.', kw, '.', value)
|
||||
else:
|
||||
return join('.', _replace_kw_in_tokens(split(content, '.'), 0, kw, value))
|
||||
|
||||
# _is-delta-value?
|
||||
def _is_delta_value_p(s):
|
||||
return ((not sx_truthy(empty_p(s))) if not sx_truthy((not sx_truthy(empty_p(s)))) else ((len(s) > 1) if not sx_truthy((len(s) > 1)) else (starts_with_p(s, '+') if sx_truthy(starts_with_p(s, '+')) else starts_with_p(s, '-'))))
|
||||
|
||||
# _apply-delta
|
||||
def _apply_delta(current_str, delta_str):
|
||||
cur = parse_int(current_str, NIL)
|
||||
delta = parse_int(delta_str, NIL)
|
||||
if sx_truthy((is_nil(cur) if sx_truthy(is_nil(cur)) else is_nil(delta))):
|
||||
return delta_str
|
||||
else:
|
||||
return sx_str((cur + delta))
|
||||
|
||||
# _apply-kw-pairs
|
||||
def _apply_kw_pairs(content, kw_pairs):
|
||||
if sx_truthy(empty_p(kw_pairs)):
|
||||
return content
|
||||
else:
|
||||
pair = first(kw_pairs)
|
||||
kw = first(pair)
|
||||
raw_val = nth(pair, 1)
|
||||
actual_val = ((lambda current: (raw_val if sx_truthy(is_nil(current)) else _apply_delta(current, raw_val)))(_find_keyword_value(content, kw)) if sx_truthy(_is_delta_value_p(raw_val)) else raw_val)
|
||||
return _apply_kw_pairs(_set_keyword_in_content(content, kw, actual_val), rest(kw_pairs))
|
||||
|
||||
# _apply-keywords-to-url
|
||||
def _apply_keywords_to_url(url, kw_pairs):
|
||||
if sx_truthy(empty_p(kw_pairs)):
|
||||
return url
|
||||
else:
|
||||
parts = _extract_innermost(url)
|
||||
new_content = _apply_kw_pairs(get(parts, 'content'), kw_pairs)
|
||||
return sx_str(get(parts, 'before'), new_content, get(parts, 'suffix'))
|
||||
|
||||
# _normalize-relative
|
||||
def _normalize_relative(url):
|
||||
if sx_truthy(starts_with_p(url, '(')):
|
||||
return url
|
||||
else:
|
||||
return sx_str('(', url, ')')
|
||||
|
||||
# resolve-relative-url
|
||||
def resolve_relative_url(current, relative):
|
||||
canonical = _normalize_relative(relative)
|
||||
rel_inner = slice(canonical, 1, (len(canonical) - 1))
|
||||
dots = _count_leading_dots(rel_inner)
|
||||
body = slice(rel_inner, _count_leading_dots(rel_inner))
|
||||
if sx_truthy((dots == 0)):
|
||||
return current
|
||||
else:
|
||||
parsed = _parse_relative_body(body)
|
||||
pos_body = get(parsed, 'positional')
|
||||
kw_pairs = get(parsed, 'keywords')
|
||||
after_nav = ((current if sx_truthy(empty_p(pos_body)) else (lambda stripped: (lambda suffix: sx_str(stripped, '.', pos_body, suffix))(slice(current, len(_strip_trailing_close(current)))))(_strip_trailing_close(current))) if sx_truthy((dots == 1)) else (lambda base: (base if sx_truthy(empty_p(pos_body)) else (sx_str('/(', pos_body, ')') if sx_truthy((base == '/')) else (lambda stripped: (lambda suffix: sx_str(stripped, '.(', pos_body, ')', suffix))(slice(base, len(_strip_trailing_close(base)))))(_strip_trailing_close(base)))))(_pop_sx_url_levels(current, (dots - 1))))
|
||||
return _apply_keywords_to_url(after_nav, kw_pairs)
|
||||
|
||||
# relative-sx-url?
|
||||
def relative_sx_url_p(url):
|
||||
return ((starts_with_p(url, '(') if not sx_truthy(starts_with_p(url, '(')) else (not sx_truthy(starts_with_p(url, '/(')))) if sx_truthy((starts_with_p(url, '(') if not sx_truthy(starts_with_p(url, '(')) else (not sx_truthy(starts_with_p(url, '/('))))) else starts_with_p(url, '.'))
|
||||
|
||||
# _url-special-forms
|
||||
def _url_special_forms():
|
||||
return ['!source', '!inspect', '!diff', '!search', '!raw', '!json']
|
||||
|
||||
# url-special-form?
|
||||
def url_special_form_p(name):
|
||||
return (starts_with_p(name, '!') if not sx_truthy(starts_with_p(name, '!')) else contains_p(_url_special_forms(), name))
|
||||
|
||||
# parse-sx-url
|
||||
def parse_sx_url(url):
|
||||
if sx_truthy((url == '/')):
|
||||
return {'type': 'home', 'raw': url}
|
||||
elif sx_truthy(relative_sx_url_p(url)):
|
||||
return {'type': 'relative', 'raw': url}
|
||||
elif sx_truthy((starts_with_p(url, '/(!') if not sx_truthy(starts_with_p(url, '/(!')) else ends_with_p(url, ')'))):
|
||||
inner = slice(url, 2, (len(url) - 1))
|
||||
dot_pos = _index_of_safe(inner, '.')
|
||||
paren_pos = _index_of_safe(inner, '(')
|
||||
end_pos = (len(inner) if sx_truthy((is_nil(dot_pos) if not sx_truthy(is_nil(dot_pos)) else is_nil(paren_pos))) else (paren_pos if sx_truthy(is_nil(dot_pos)) else (dot_pos if sx_truthy(is_nil(paren_pos)) else min(dot_pos, paren_pos))))
|
||||
form_name = slice(inner, 0, end_pos)
|
||||
rest_part = slice(inner, end_pos)
|
||||
inner_expr = (slice(rest_part, 1) if sx_truthy(starts_with_p(rest_part, '.')) else rest_part)
|
||||
return {'type': 'special-form', 'form': form_name, 'inner': inner_expr, 'raw': url}
|
||||
elif sx_truthy((starts_with_p(url, '/(~') if not sx_truthy(starts_with_p(url, '/(~')) else ends_with_p(url, ')'))):
|
||||
name = slice(url, 2, (len(url) - 1))
|
||||
return {'type': 'direct-component', 'name': name, 'raw': url}
|
||||
elif sx_truthy((starts_with_p(url, '/(') if not sx_truthy(starts_with_p(url, '/(')) else ends_with_p(url, ')'))):
|
||||
return {'type': 'absolute', 'raw': url}
|
||||
else:
|
||||
return {'type': 'path', 'raw': url}
|
||||
|
||||
# url-special-form-name
|
||||
def url_special_form_name(url):
|
||||
parsed = parse_sx_url(url)
|
||||
if sx_truthy((get(parsed, 'type') == 'special-form')):
|
||||
return get(parsed, 'form')
|
||||
else:
|
||||
return NIL
|
||||
|
||||
# url-special-form-inner
|
||||
def url_special_form_inner(url):
|
||||
parsed = parse_sx_url(url)
|
||||
if sx_truthy((get(parsed, 'type') == 'special-form')):
|
||||
return get(parsed, 'inner')
|
||||
else:
|
||||
return NIL
|
||||
|
||||
# url-to-expr
|
||||
def url_to_expr(url_path):
|
||||
if sx_truthy(((url_path == '/') if sx_truthy((url_path == '/')) else empty_p(url_path))):
|
||||
return []
|
||||
else:
|
||||
trimmed = (slice(url_path, 1) if sx_truthy(starts_with_p(url_path, '/')) else url_path)
|
||||
sx_source = replace(trimmed, '.', ' ')
|
||||
exprs = sx_parse(sx_source)
|
||||
if sx_truthy(empty_p(exprs)):
|
||||
return []
|
||||
else:
|
||||
return first(exprs)
|
||||
|
||||
# auto-quote-unknowns
|
||||
def auto_quote_unknowns(expr, env):
|
||||
if sx_truthy((not sx_truthy(list_p(expr)))):
|
||||
return expr
|
||||
else:
|
||||
if sx_truthy(empty_p(expr)):
|
||||
return expr
|
||||
else:
|
||||
return cons(first(expr), map(lambda child: (auto_quote_unknowns(child, env) if sx_truthy(list_p(child)) else ((lambda name: (child if sx_truthy((env_has(env, name) if sx_truthy(env_has(env, name)) else (starts_with_p(name, ':') if sx_truthy(starts_with_p(name, ':')) else (starts_with_p(name, '~') if sx_truthy(starts_with_p(name, '~')) else starts_with_p(name, '!'))))) else name))(symbol_name(child)) if sx_truthy((type_of(child) == 'symbol')) else child)), rest(expr)))
|
||||
|
||||
# prepare-url-expr
|
||||
def prepare_url_expr(url_path, env):
|
||||
expr = url_to_expr(url_path)
|
||||
if sx_truthy(empty_p(expr)):
|
||||
return expr
|
||||
else:
|
||||
return auto_quote_unknowns(expr, env)
|
||||
|
||||
|
||||
# === Transpiled from signals (reactive signal runtime) ===
|
||||
|
||||
# signal
|
||||
|
||||
Reference in New Issue
Block a user