GUEST: step 4 — lib/guest/pratt.sx operator-table format + lookup
Extracted the data-half of Pratt-style precedence parsing: the operator table format and lookup. The climbing loop stays per-language because the two canaries use opposite conventions (lua: higher prec = tighter; prolog: lower prec = tighter, with xfx/xfy/yfx assoc tags) — forcing one shared loop adds callback indirection that obscures more than it shares. The brief's literal ask is "Grammar is a dict, not hardcoded cond" and that's what gets shared. Entry shape: (NAME PREC ASSOC). Three accessors: pratt-op-name / pratt-op-prec / pratt-op-assoc. One traversal: pratt-op-lookup. Ported lua/parser.sx — replaced 18-clause cond and the lua-binop-right? hardcoded `or` with a 15-entry lua-op-table, now queried via pratt-op-lookup. Ported prolog/parser.sx — pl-op-find (linear walk reimpl) deleted; pl-op-lookup wraps pratt-op-lookup; pl-token-op simplified to return the entry directly. Verification: - lua/test.sh: 185/185 = baseline. - prolog/conformance.sh: 590/590 = baseline (timestamp-only diff). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
28
lib/guest/pratt.sx
Normal file
28
lib/guest/pratt.sx
Normal file
@@ -0,0 +1,28 @@
|
||||
;; lib/guest/pratt.sx — operator-table format + lookup for Pratt-style
|
||||
;; precedence climbing.
|
||||
;;
|
||||
;; The climbing loop stays per-language because the two canaries use
|
||||
;; opposite conventions (Lua: higher prec = tighter; Prolog: lower prec =
|
||||
;; tighter, with xfx/xfy/yfx assoc tags). Forcing a single loop adds
|
||||
;; callback indirection that obscures more than it shares.
|
||||
;;
|
||||
;; What IS shared and gets extracted: the operator-table format and lookup.
|
||||
;; "Grammar is a dict, not hardcoded cond."
|
||||
;;
|
||||
;; Entry shape: (NAME PREC ASSOC).
|
||||
;; NAME — string, the operator's source token.
|
||||
;; PREC — integer, in the host's own convention.
|
||||
;; ASSOC — :left | :right | :none for languages with traditional
|
||||
;; associativity, or "xfx" / "xfy" / "yfx" for Prolog-style.
|
||||
|
||||
(define
|
||||
pratt-op-lookup
|
||||
(fn (table name)
|
||||
(cond
|
||||
((empty? table) nil)
|
||||
((= (first (first table)) name) (first table))
|
||||
(:else (pratt-op-lookup (rest table) name)))))
|
||||
|
||||
(define pratt-op-name (fn (entry) (first entry)))
|
||||
(define pratt-op-prec (fn (entry) (nth entry 1)))
|
||||
(define pratt-op-assoc (fn (entry) (nth entry 2)))
|
||||
@@ -3,28 +3,33 @@
|
||||
(define lua-tok-value (fn (t) (if (= t nil) nil (get t :value))))
|
||||
|
||||
(define
|
||||
lua-binop-prec
|
||||
(fn
|
||||
(op)
|
||||
(cond
|
||||
((= op "or") 1)
|
||||
((= op "and") 2)
|
||||
((= op "<") 3)
|
||||
((= op ">") 3)
|
||||
((= op "<=") 3)
|
||||
((= op ">=") 3)
|
||||
((= op "==") 3)
|
||||
((= op "~=") 3)
|
||||
((= op "..") 5)
|
||||
((= op "+") 6)
|
||||
((= op "-") 6)
|
||||
((= op "*") 7)
|
||||
((= op "/") 7)
|
||||
((= op "%") 7)
|
||||
((= op "^") 10)
|
||||
(else 0))))
|
||||
lua-op-table
|
||||
(list
|
||||
(list "or" 1 :left)
|
||||
(list "and" 2 :left)
|
||||
(list "<" 3 :left)
|
||||
(list ">" 3 :left)
|
||||
(list "<=" 3 :left)
|
||||
(list ">=" 3 :left)
|
||||
(list "==" 3 :left)
|
||||
(list "~=" 3 :left)
|
||||
(list ".." 5 :right)
|
||||
(list "+" 6 :left)
|
||||
(list "-" 6 :left)
|
||||
(list "*" 7 :left)
|
||||
(list "/" 7 :left)
|
||||
(list "%" 7 :left)
|
||||
(list "^" 10 :right)))
|
||||
|
||||
(define lua-binop-right? (fn (op) (or (= op "..") (= op "^"))))
|
||||
(define lua-binop-prec
|
||||
(fn (op)
|
||||
(let ((entry (pratt-op-lookup lua-op-table op)))
|
||||
(if (= entry nil) 0 (pratt-op-prec entry)))))
|
||||
|
||||
(define lua-binop-right?
|
||||
(fn (op)
|
||||
(let ((entry (pratt-op-lookup lua-op-table op)))
|
||||
(and (not (= entry nil)) (= (pratt-op-assoc entry) :right)))))
|
||||
|
||||
(define
|
||||
lua-parse
|
||||
|
||||
@@ -30,6 +30,7 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(epoch 1)
|
||||
(load "lib/guest/lex.sx")
|
||||
(load "lib/guest/prefix.sx")
|
||||
(load "lib/guest/pratt.sx")
|
||||
(load "lib/lua/tokenizer.sx")
|
||||
(epoch 2)
|
||||
(load "lib/lua/parser.sx")
|
||||
|
||||
@@ -4,6 +4,7 @@ LANG_NAME=prolog
|
||||
MODE=dict
|
||||
|
||||
PRELOADS=(
|
||||
lib/guest/pratt.sx
|
||||
lib/prolog/tokenizer.sx
|
||||
lib/prolog/parser.sx
|
||||
lib/prolog/runtime.sx
|
||||
|
||||
@@ -104,18 +104,9 @@
|
||||
(list ":-" 1200 "xfx")
|
||||
(list "mod" 400 "yfx")))
|
||||
|
||||
(define
|
||||
pl-op-find
|
||||
(fn
|
||||
(name table)
|
||||
(cond
|
||||
((empty? table) nil)
|
||||
((= (first (first table)) name) (rest (first table)))
|
||||
(true (pl-op-find name (rest table))))))
|
||||
(define pl-op-lookup (fn (name) (pratt-op-lookup pl-op-table name)))
|
||||
|
||||
(define pl-op-lookup (fn (name) (pl-op-find name pl-op-table)))
|
||||
|
||||
;; Token → (name prec type) for known infix ops, else nil.
|
||||
;; Token → entry (name prec type) for known infix ops, else nil.
|
||||
(define
|
||||
pl-token-op
|
||||
(fn
|
||||
@@ -123,14 +114,8 @@
|
||||
(let
|
||||
((ty (get t :type)) (vv (get t :value)))
|
||||
(cond
|
||||
((and (= ty "punct") (= vv ","))
|
||||
(let
|
||||
((info (pl-op-lookup ",")))
|
||||
(if (nil? info) nil (cons "," info))))
|
||||
((or (= ty "atom") (= ty "op"))
|
||||
(let
|
||||
((info (pl-op-lookup vv)))
|
||||
(if (nil? info) nil (cons vv info))))
|
||||
((and (= ty "punct") (= vv ",")) (pl-op-lookup ","))
|
||||
((or (= ty "atom") (= ty "op")) (pl-op-lookup vv))
|
||||
(true nil)))))
|
||||
|
||||
;; ── Term parser ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"total_failed": 0,
|
||||
"total": 590,
|
||||
"suites": {"parse":{"passed":25,"total":25,"failed":0},"unify":{"passed":47,"total":47,"failed":0},"clausedb":{"passed":14,"total":14,"failed":0},"solve":{"passed":62,"total":62,"failed":0},"operators":{"passed":19,"total":19,"failed":0},"dynamic":{"passed":11,"total":11,"failed":0},"findall":{"passed":11,"total":11,"failed":0},"term_inspect":{"passed":14,"total":14,"failed":0},"append":{"passed":6,"total":6,"failed":0},"reverse":{"passed":6,"total":6,"failed":0},"member":{"passed":7,"total":7,"failed":0},"nqueens":{"passed":6,"total":6,"failed":0},"family":{"passed":10,"total":10,"failed":0},"atoms":{"passed":34,"total":34,"failed":0},"query_api":{"passed":16,"total":16,"failed":0},"iso_predicates":{"passed":29,"total":29,"failed":0},"meta_predicates":{"passed":25,"total":25,"failed":0},"list_predicates":{"passed":33,"total":33,"failed":0},"meta_call":{"passed":15,"total":15,"failed":0},"set_predicates":{"passed":15,"total":15,"failed":0},"char_predicates":{"passed":27,"total":27,"failed":0},"io_predicates":{"passed":24,"total":24,"failed":0},"assert_rules":{"passed":15,"total":15,"failed":0},"string_agg":{"passed":25,"total":25,"failed":0},"advanced":{"passed":21,"total":21,"failed":0},"compiler":{"passed":17,"total":17,"failed":0},"cross_validate":{"passed":17,"total":17,"failed":0},"integration":{"passed":20,"total":20,"failed":0},"hs_bridge":{"passed":19,"total":19,"failed":0}},
|
||||
"generated": "2026-05-06T22:23:38+00:00"
|
||||
"generated": "2026-05-07T17:07:57+00:00"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Prolog scoreboard
|
||||
|
||||
**590 / 590 passing** (0 failure(s)).
|
||||
Generated 2026-05-06T22:23:38+00:00.
|
||||
Generated 2026-05-07T17:07:57+00:00.
|
||||
|
||||
| Suite | Passed | Total | Status |
|
||||
|-------|--------|-------|--------|
|
||||
|
||||
Reference in New Issue
Block a user