Defines the 10 canonical node kinds called out in the brief — literal,
var, app, lambda, let, letrec, if, match-clause, module, import — plus
predicates, ast-kind dispatch, and per-field accessors. Each node is a
tagged keyword-headed list: (:literal V), (:var N), (:app FN ARGS), …
Also lib/guest/tests/ast.sx — 33 tests exercising every constructor +
predicate + accessor, runnable via (gast-tests-run!) which returns the
{:passed :failed :total} dict the shared conformance driver expects.
PARTIAL — pending real consumers. The brief calls Step 5 "Optional —
guests may keep their own AST" and forcing lua/prolog to switch their
internal AST shape risks regressing 775 passing tests for tooling that
nothing yet calls. Both internal ASTs are untouched; lua still 185/185,
prolog still 590/590. Datalog-on-sx (in flight, see plans/datalog-on-sx.md)
will be the natural first real consumer; lua/prolog converters can land
when a cross-language tool wants them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
93 lines
4.3 KiB
Plaintext
93 lines
4.3 KiB
Plaintext
;; lib/guest/ast.sx — canonical AST node shapes.
|
|
;;
|
|
;; A guest's parser may emit its own AST in whatever shape is convenient
|
|
;; for that language's evaluator/transpiler. This file gives a SHARED
|
|
;; canonical shape that cross-language tools (formatters, highlighters,
|
|
;; debuggers) can target without per-language adapters.
|
|
;;
|
|
;; Each canonical node is a tagged list: (KIND ...payload).
|
|
;;
|
|
;; Constructors (return a canonical node):
|
|
;;
|
|
;; (ast-literal VALUE) — number / string / bool / nil
|
|
;; (ast-var NAME) — identifier reference
|
|
;; (ast-app FN ARGS) — function application
|
|
;; (ast-lambda PARAMS BODY) — anonymous function
|
|
;; (ast-let BINDINGS BODY) — local bindings
|
|
;; (ast-letrec BINDINGS BODY) — recursive local bindings
|
|
;; (ast-if TEST THEN ELSE) — conditional
|
|
;; (ast-match-clause PATTERN BODY) — one match arm
|
|
;; (ast-module NAME BODY) — module declaration
|
|
;; (ast-import NAME) — import directive
|
|
;;
|
|
;; Predicates: (ast-literal? X), (ast-var? X), …
|
|
;; Generic: (ast? X) — any canonical node
|
|
;; (ast-kind X) — :literal / :var / :app / …
|
|
;;
|
|
;; Accessors (one per payload field):
|
|
;; (ast-literal-value N)
|
|
;; (ast-var-name N)
|
|
;; (ast-app-fn N) (ast-app-args N)
|
|
;; (ast-lambda-params N) (ast-lambda-body N)
|
|
;; (ast-let-bindings N) (ast-let-body N)
|
|
;; (ast-letrec-bindings N) (ast-letrec-body N)
|
|
;; (ast-if-test N) (ast-if-then N) (ast-if-else N)
|
|
;; (ast-match-clause-pattern N)
|
|
;; (ast-match-clause-body N)
|
|
;; (ast-module-name N) (ast-module-body N)
|
|
;; (ast-import-name N)
|
|
|
|
(define ast-literal (fn (v) (list :literal v)))
|
|
(define ast-var (fn (n) (list :var n)))
|
|
(define ast-app (fn (f args) (list :app f args)))
|
|
(define ast-lambda (fn (ps body) (list :lambda ps body)))
|
|
(define ast-let (fn (bs body) (list :let bs body)))
|
|
(define ast-letrec (fn (bs body) (list :letrec bs body)))
|
|
(define ast-if (fn (t th el) (list :if t th el)))
|
|
(define ast-match-clause (fn (p body) (list :match-clause p body)))
|
|
(define ast-module (fn (n body) (list :module n body)))
|
|
(define ast-import (fn (n) (list :import n)))
|
|
|
|
(define ast-kind (fn (x) (if (and (list? x) (not (empty? x))) (first x) nil)))
|
|
|
|
(define
|
|
ast?
|
|
(fn (x)
|
|
(and (list? x)
|
|
(not (empty? x))
|
|
(let ((k (first x)))
|
|
(or (= k :literal) (= k :var) (= k :app)
|
|
(= k :lambda) (= k :let) (= k :letrec)
|
|
(= k :if) (= k :match-clause)
|
|
(= k :module) (= k :import))))))
|
|
|
|
(define ast-literal? (fn (x) (and (ast? x) (= (first x) :literal))))
|
|
(define ast-var? (fn (x) (and (ast? x) (= (first x) :var))))
|
|
(define ast-app? (fn (x) (and (ast? x) (= (first x) :app))))
|
|
(define ast-lambda? (fn (x) (and (ast? x) (= (first x) :lambda))))
|
|
(define ast-let? (fn (x) (and (ast? x) (= (first x) :let))))
|
|
(define ast-letrec? (fn (x) (and (ast? x) (= (first x) :letrec))))
|
|
(define ast-if? (fn (x) (and (ast? x) (= (first x) :if))))
|
|
(define ast-match-clause? (fn (x) (and (ast? x) (= (first x) :match-clause))))
|
|
(define ast-module? (fn (x) (and (ast? x) (= (first x) :module))))
|
|
(define ast-import? (fn (x) (and (ast? x) (= (first x) :import))))
|
|
|
|
(define ast-literal-value (fn (n) (nth n 1)))
|
|
(define ast-var-name (fn (n) (nth n 1)))
|
|
(define ast-app-fn (fn (n) (nth n 1)))
|
|
(define ast-app-args (fn (n) (nth n 2)))
|
|
(define ast-lambda-params (fn (n) (nth n 1)))
|
|
(define ast-lambda-body (fn (n) (nth n 2)))
|
|
(define ast-let-bindings (fn (n) (nth n 1)))
|
|
(define ast-let-body (fn (n) (nth n 2)))
|
|
(define ast-letrec-bindings (fn (n) (nth n 1)))
|
|
(define ast-letrec-body (fn (n) (nth n 2)))
|
|
(define ast-if-test (fn (n) (nth n 1)))
|
|
(define ast-if-then (fn (n) (nth n 2)))
|
|
(define ast-if-else (fn (n) (nth n 3)))
|
|
(define ast-match-clause-pattern (fn (n) (nth n 1)))
|
|
(define ast-match-clause-body (fn (n) (nth n 2)))
|
|
(define ast-module-name (fn (n) (nth n 1)))
|
|
(define ast-module-body (fn (n) (nth n 2)))
|
|
(define ast-import-name (fn (n) (nth n 1)))
|