From e9509ca1261e34fde9652dba185d66cbc56e5597 Mon Sep 17 00:00:00 2001 From: Jakob L. Kreuze Date: Sun, 16 Apr 2023 19:14:17 -0400 Subject: GOOPS rewrite --- compiler/lean.scm | 18 +- compiler/main.scm | 19 +- compiler/passes/compile-exists-some.scm | 22 +- compiler/passes/compile-funcall.scm | 2 +- compiler/passes/emit-specification.scm | 49 +++-- compiler/passes/extract-identifiers.scm | 21 +- compiler/passes/parse-policy.scm | 47 +++-- compiler/passes/preprocess-policy.scm | 106 +++++----- compiler/types.scm | 348 +++++++++++++++----------------- compiler/utilities.scm | 48 ++++- 10 files changed, 370 insertions(+), 310 deletions(-) diff --git a/compiler/lean.scm b/compiler/lean.scm index 41d4c92..7c1e226 100644 --- a/compiler/lean.scm +++ b/compiler/lean.scm @@ -73,10 +73,24 @@ (body #:init-keyword #:body #:getter body)) +(define-method (tree-map (form ) f scope breadcrumb) + (match form + (($ variables body) + (let ((inner (with-modified-slot form 'body + (tree-map body f scope breadcrumb)))) + (f scope breadcrumb inner))))) + (define-class () (clauses #:init-keyword #:clauses #:getter clauses)) +(define-method (tree-map (form ) f scope breadcrumb) + (match form + (($ clauses) + (let ((inner (with-modified-slot form 'clauses + (tree-map clauses f scope breadcrumb)))) + (f scope breadcrumb inner))))) + (define-class () (kind #:init-keyword #:kind #:getter kind) @@ -226,7 +240,7 @@ (match reference (($ (x)) (sanitize x)) (($ (x xs ...)) - (if (and (equal? x (make-variable-ref "internal")) - (equal? xs (list (make-constant "member_2")))) + (if (and (equal? (to-pretty-sexp x) `( "internal")) + (equal? (to-pretty-sexp xs) (list `( "member_2")))) "Array.contains" (error "Unimplemented"))))) diff --git a/compiler/main.scm b/compiler/main.scm index 387aed7..91fc611 100644 --- a/compiler/main.scm +++ b/compiler/main.scm @@ -23,6 +23,7 @@ #:use-module (compiler passes parse-policy) #:use-module (compiler passes preprocess-policy) #:use-module (compiler passes read-and-parse) + #:use-module (compiler utilities) #:use-module (ice-9 pretty-print) #:use-module (srfi srfi-1) #:use-module (srfi srfi-11) @@ -45,18 +46,6 @@ of `(program-arguments)'." ((policy) (parse-policy package imports rules)) ((policy) (extract-identifiers policy)) ((policy) (compile-exists-some policy)) - ((policy) (compile-funcall policy)) - ;; ((policy) (compile-ref policy)) - ) - (pretty-print policy) - ;; (emit-specification policy) - ))) - -(let*-values (((ast) (read-and-parse "/home/jakob/Research/rego-proofs/examples/03-set-defined-by-predicate.rego")) - ((package imports rules) (preprocess-policy ast)) - ((policy) (parse-policy package imports rules)) - ((policy) (extract-identifiers policy)) - ((policy) (compile-constraints policy)) - ((policy) (compile-exists-some policy)) - ((policy) (compile-funcall policy))) - (emit-specification policy)) + ((policy) (compile-funcall policy))) + ;; (pretty-print (to-pretty-sexp policy)) + (emit-specification policy)))) diff --git a/compiler/passes/compile-exists-some.scm b/compiler/passes/compile-exists-some.scm index 6bdd643..5af0245 100644 --- a/compiler/passes/compile-exists-some.scm +++ b/compiler/passes/compile-exists-some.scm @@ -24,6 +24,9 @@ #:use-module (srfi srfi-1) #:export (compile-exists-some)) +(define (exists-some? form) + (is-a? form )) + (define (partition-on-quantification xs) (define (recur result running cur) (cond ((null? cur) @@ -36,29 +39,30 @@ (define (exists-some->variables identifiers) (map (lambda (x) - (cond ((variable-ref? x) x) + (cond ((is-a? x ) x) ;; TODO: Assert that this is internal.member2 - ((funcall? x) (first (funcall-args x))) + ((is-a? x ) (first (args x))) (else (error "unimplement")))) - (exists-some-identifiers identifiers))) + identifiers)) (define (exists-some->constraints identifiers) (fold append '() (map (lambda (x) - (cond ((variable-ref? x) '()) - ((funcall? x) (list x)) + (cond ((is-a? x ) '()) + ((is-a? x ) (list x)) (else (error "unimplement")))) - (exists-some-identifiers identifiers)))) + identifiers))) (define (process-quantified-extent xs) (assert (list? xs)) (if (and (positive? (length xs)) (exists-some? (first xs))) (make - #:variables (exists-some->variables (first xs)) + #:variables (exists-some->variables (identifiers (first xs))) #:body (make - #:clauses (append (exists-some->constraints (first xs)) (drop xs 1)))) + #:clauses (append (exists-some->constraints (identifiers (first xs))) + (drop xs 1)))) xs)) (define (process-all form) @@ -66,7 +70,7 @@ (map process-quantified-extent (partition-on-quantification form))) (define (compile-exists-some policy) - (mutate-tree + (policy-map policy (lambda (scope breadcrumb form) (if (and (list? form) (any exists-some? form)) diff --git a/compiler/passes/compile-funcall.scm b/compiler/passes/compile-funcall.scm index 2e170c0..04444ad 100644 --- a/compiler/passes/compile-funcall.scm +++ b/compiler/passes/compile-funcall.scm @@ -24,7 +24,7 @@ #:export (compile-funcall)) (define (compile-funcall policy) - (mutate-tree + (policy-map policy (lambda (scope breadcrumb form) (match form diff --git a/compiler/passes/emit-specification.scm b/compiler/passes/emit-specification.scm index f2290e4..6af8776 100644 --- a/compiler/passes/emit-specification.scm +++ b/compiler/passes/emit-specification.scm @@ -27,10 +27,9 @@ #:export (emit-specification)) (define (reference->string reference) - (string-join (map (lambda (term) - (cond ((variable-ref? term) (variable-ref-name term)) - (else (error "unimplemented")))) - (reference-parts reference)) ".")) + (string-join (map (match-lambda + (($ name) name)) + (inner reference)) ".")) (define (compile-expr expr) (match expr @@ -47,23 +46,22 @@ (_ expr))) (define (compile-rule rule) - (assert (rule? rule)) + (assert (is-a? rule )) (match rule - (($ default head body) - (let ((conjunction (body-conjunction body))) - (make - #:conclusion (compile-expr (head-rhs head)) - #:body (if (> (length conjunction) 1) - (make #:clauses (map compile-expr conjunction)) - (compile-expr (first conjunction)))))))) + (($ default head ($ conjunction)) + (make + #:conclusion (compile-expr (right head)) + #:body (if (> (length conjunction) 1) + (make #:clauses (map compile-expr conjunction)) + (compile-expr (first conjunction))))))) (define (compile-rule-group group) (match group ((left rules ...) - (assert (reference? left)) + (assert (is-a? left )) (make ;; TODO: Not really correct now, is it? - #:kind (head-kind (rule-head (first rules))) + #:kind (kind (head (first rules))) #:name (reference->string left) ;; TODO: Find unique name #:subject "v" @@ -71,14 +69,15 @@ (define (emit-specification policy) "Emit a Lean file specifying the semantics of `policy'." - (assert (policy? policy)) - (format #t "-- package ~a~%" (policy-package policy)) - (newline) - (for-each (cut format #t "-- imports ~a~%" <>) - (policy-imports policy)) - (newline) - (for-each (lambda (x) - (display (compile x)) - (newline)) - (map compile-rule-group (policy-rules policy))) - (newline)) + (assert (is-a? policy )) + (match policy + (($ package imports _ rules _) + (format #t "-- package ~a~%" package) + (newline) + (for-each (cut format #t "-- imports ~a~%" <>) imports) + (newline) + (for-each (lambda (x) + (display (compile x)) + (newline)) + (map compile-rule-group rules)) + (newline)))) diff --git a/compiler/passes/extract-identifiers.scm b/compiler/passes/extract-identifiers.scm index 1420176..7a6cb10 100644 --- a/compiler/passes/extract-identifiers.scm +++ b/compiler/passes/extract-identifiers.scm @@ -20,6 +20,7 @@ #:use-module (ice-9 match) #:use-module (ice-9 regex) #:use-module (lens) + #:use-module (oop goops) #:use-module (srfi srfi-1) #:use-module (srfi srfi-69) #:export (extract-identifiers)) @@ -74,7 +75,7 @@ ;; TODO: In what cases do we error out, instead of treating this as a free variable? (($ name) - `((,(make-reference (list (make-variable-ref name))) any))) + `((,(make #:parts (list (make #:name name))) any))) (($ _ args) (recur breadcrumb args)) @@ -87,11 +88,11 @@ ;; `recur' won't encounter heads. Similarly, policy documents can't be nested. (_ '()))) (for-each (lambda (rule) - (hash-table-append-item! scopes '() `(,(head-lhs (rule-head rule)) any)) - (recur (list (head-lhs (rule-head rule))) - (rule-body rule))) - (map cadr (policy-rules policy))) - (make-scope scopes)) + (hash-table-append-item! scopes '() `(,(left (head rule)) any)) + (recur (list (left (head rule))) + (body rule))) + (map cadr (rules policy))) + (make #:identifiers scopes)) (define (extract-identifiers policy) "Annotate `policy' with any variable occurrences @@ -105,9 +106,9 @@ aren't. This is updated in later passes." (define (my-rewrite-anonymous form) (match form (($ name) - (make-variable-ref - (if (string= name "$0") - "anony_0" - name))) + (make + #:name (if (string= name "$0") + "anony_0" + name))) (anything-else anything-else))) diff --git a/compiler/passes/parse-policy.scm b/compiler/passes/parse-policy.scm index 280d366..ac5e3d8 100644 --- a/compiler/passes/parse-policy.scm +++ b/compiler/passes/parse-policy.scm @@ -15,10 +15,11 @@ ;;; . (define-module (compiler passes parse-policy) + #:use-module ((rnrs base) #:select (assert)) #:use-module (compiler types) #:use-module (compiler utilities) #:use-module (ice-9 match) - #:use-module ((rnrs base) #:select (assert)) + #:use-module (oop goops) #:use-module (srfi srfi-1) #:use-module (srfi srfi-2) #:use-module (srfi srfi-26) @@ -27,13 +28,13 @@ (define (extract-rules rules) "Group rules in `ast' by name, parsing each into a record" - (define targets (map (lambda (rule) (head-lhs (second rule))) rules)) + (define targets (map (lambda (rule) (left (second rule))) rules)) (define result (map (lambda (target) (cons target (map (lambda (x) (apply extract-rule x)) (filter (lambda (rule) - (equal? target (head-lhs (second rule)))) + (equal? target (left (second rule)))) rules)))) (delete-duplicates! targets))) @@ -44,28 +45,36 @@ (define (extract-rule default head body) "Extract an individual parsed rule" - (make-rule default head (extract-body body))) + (make + #:default default + #:head head + #:body (extract-body body))) (define (extract-body body-obj) "Extract the body of a parsed rule" - (assert (body? body-obj)) - (let ((conjunction (body-conjunction body-obj))) + (assert (is-a? body-obj )) + (let ((conjunction (inner body-obj))) ;; Every part of the conjunction is an alist with a `terms' field. (assert (every alist? conjunction)) (assert (every (lambda (part) (assoc "terms" part)) conjunction)) - (make-body (map (lambda (part) (maybe-extract-for-all (assoc-ref part "terms"))) conjunction)))) + (make + #:conjunction + (map (lambda (part) + (maybe-extract-for-all (assoc-ref part "terms"))) + conjunction)))) (define (maybe-extract-for-all term-obj) (if (and (alist? term-obj) (assoc "domain" term-obj)) - (make-for-all (assoc-ref term-obj "domain") - (assoc-ref term-obj "key") - (assoc-ref term-obj "value") - (extract-body (make-body (assoc-ref term-obj "body")))) + (make + #:domain (assoc-ref term-obj "domain") + #:key (assoc-ref term-obj "key") + #:value (assoc-ref term-obj "value") + #:body (extract-body (make #:conjunction (assoc-ref term-obj "body")))) (maybe-extract-exists-some term-obj))) (define (maybe-extract-exists-some term-obj) (if (and (alist? term-obj) (assoc "symbols" term-obj)) - (make-exists-some (assoc-ref term-obj "symbols")) + (make #:identifiers (assoc-ref term-obj "symbols")) term-obj)) (define (extract-term term-obj) @@ -79,18 +88,22 @@ (_ (extract-constant term-obj)))) (define (extract-constant term-obj) - (make-constant (assoc-ref term-obj "value"))) + (make #:value (assoc-ref term-obj "value"))) (define (extract-variable-ref term-obj) - (make-variable-ref (assoc-ref term-obj "value"))) + (make #:name (assoc-ref term-obj "value"))) (define (extract-reference reference-obj) - (make-reference - (map extract-literal (array->list reference-obj)))) + (make + #:parts + (map extract-literal (array->list reference-obj)))) (define (parse-policy package imports rules) "Parse `ast', an abstract syntax tree from OPA, into a `'" (assert (string? package)) (assert (and (list? imports) (every string? imports))) (assert (list? rules)) - (make-policy package imports #f (extract-rules rules) #f)) + (make + #:package package + #:imports imports + #:rules (extract-rules rules))) diff --git a/compiler/passes/preprocess-policy.scm b/compiler/passes/preprocess-policy.scm index 17a7318..a743766 100644 --- a/compiler/passes/preprocess-policy.scm +++ b/compiler/passes/preprocess-policy.scm @@ -15,10 +15,11 @@ ;;; . (define-module (compiler passes preprocess-policy) + #:use-module ((rnrs base) #:select (assert)) #:use-module (compiler types) #:use-module (compiler utilities) #:use-module (ice-9 match) - #:use-module ((rnrs base) #:select (assert)) + #:use-module (oop goops) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:export (preprocess-policy @@ -68,7 +69,8 @@ (map (lambda (rule) (list (assoc-ref rule "default") (preprocess-head (assoc-ref rule "head")) - (make-body (preprocess-body (assoc-ref rule "body"))))) + (make + #:conjunction (preprocess-body (assoc-ref rule "body"))))) rules)) (define (determine-head-kind head-obj) @@ -86,16 +88,17 @@ field-eqv: assignment to a reference (varname[field] IS value IF condition) (else (error "unidentified rule kind" head-obj)))) (define (preprocess-head head-obj) - (define kind (determine-head-kind head-obj)) ;; Head rules will sometimes have an "assign" key, but this is only when the ;; right-hand side is a scalar value. - (make-head (assoc-ref head-obj "name") - kind - (parse-reference (assoc-ref head-obj "ref")) - (parse-literal - (if (eqv? kind 'set-memq) - (assoc-ref head-obj "key") - (assoc-ref head-obj "value"))))) + (let ((kind (determine-head-kind head-obj))) + (make + #:name (assoc-ref head-obj "name") + #:kind kind + #:lhs (parse-reference (assoc-ref head-obj "ref")) + #:rhs (parse-literal + (if (eqv? kind 'set-memq) + (assoc-ref head-obj "key") + (assoc-ref head-obj "value")))))) (define (preprocess-body body-obj) (cond ;; Non-array strings should be turned into lists. @@ -113,7 +116,7 @@ field-eqv: assignment to a reference (varname[field] IS value IF condition) ;; Recurse on list. ((list? body-obj) (map preprocess-body body-obj)) ;; Look inside the `identifiers' of an `' - ((exists-some? body-obj) (preprocess-body (exists-some-identifiers body-obj))) + ((is-a? body-obj ) (preprocess-body (identifiers body-obj))) ;; Anything else is the identity. (else body-obj))) @@ -121,60 +124,67 @@ field-eqv: assignment to a reference (varname[field] IS value IF condition) "Parse into a Scheme record `term-obj' containing `type,' `value' keys" (match (assoc-ref term-obj "type") ("var" (parse-variable-ref term-obj)) - ("ref" (make-reference (assoc-ref term-obj "value"))) + ("ref" (make #:parts (assoc-ref term-obj "value"))) ("call" (let ((parts (assoc-ref term-obj "value"))) - (make-funcall (first parts) (drop parts 1)))) + (make #:name (first parts) #:args (drop parts 1)))) (_ (parse-constant term-obj)))) (define (pairs->object-literal pairs) (let* ((pairs (array->list pairs)) (pairs (map array->list pairs))) - (make-object-literal - (map (lambda (pair) - ;; Assumption that the object is represented as pairs of constant - ;; objects where the first is the key name and the second is the - ;; value. - (assert (string= "string" (assoc-ref (first pair) "type"))) - (let ((key (assoc-ref (first pair) "value"))) - (cons key (parse-literal (second pair))))) - pairs)))) + (make + #:pairs + (map (lambda (pair) + ;; Assumption that the object is represented as pairs of constant + ;; objects where the first is the key name and the second is the + ;; value. + (assert (string= "string" (assoc-ref (first pair) "type"))) + (let ((key (assoc-ref (first pair) "value"))) + (cons key (parse-literal (second pair))))) + pairs)))) (define (parse-constant term-obj) "Parse a constant literal (boolean, number, string, array, set, or object)" (let ((type (assoc-ref term-obj "type")) (value (assoc-ref term-obj "value"))) - (make-constant (cond ((or (boolean? value) - (number? value) - (string? value)) value) - ((string= "array" type) (map parse-literal (array->list value))) - ((string= "set" type) (make-set-literal (map parse-literal (array->list value)))) - ((string= "object" type) (pairs->object-literal value)) - ((string= "arraycomprehension" type) - (make-comprehension 'array - (parse-constant (assoc-ref value "term")) - #f - (parse-constant (assoc-ref value "body")))) - ((string= "setcomprehension" type) - (make-comprehension 'set - (parse-constant (assoc-ref value "term")) - #f - (parse-constant (assoc-ref value "body")))) - ((string= "objectcomprehension" type) - (make-comprehension 'object - (parse-constant (assoc-ref value "key")) - (parse-constant (assoc-ref value "value")) - (parse-constant (assoc-ref value "body")))) - ;; (else (error "unidentified type" type)) - (else value) - )))) + (make + #:value + (cond ((or (boolean? value) + (number? value) + (string? value)) value) + ((string= "array" type) (map parse-literal (array->list value))) + ((string= "set" type) (make + #:elems (map parse-literal (array->list value)))) + ((string= "object" type) (pairs->object-literal value)) + ((string= "arraycomprehension" type) + (make + #:kind 'array + #:key (parse-constant (assoc-ref value "term")) + #:value #f + #:body (parse-constant (assoc-ref value "body")))) + ((string= "setcomprehension" type) + (make + #:kind 'set + #:key (parse-constant (assoc-ref value "term")) + #:value #f + #:body (parse-constant (assoc-ref value "body")))) + ((string= "objectcomprehension" type) + (make + #:kind 'object + #:key (parse-constant (assoc-ref value "key")) + #:value (parse-constant (assoc-ref value "value")) + #:body (parse-constant (assoc-ref value "body")))) + ;; (else (error "unidentified type" type)) + (else value) + )))) (define (parse-variable-ref term-obj) "Parse a reference to a named variable" - (make-variable-ref (assoc-ref term-obj "value"))) + (make #:name (assoc-ref term-obj "value"))) (define (parse-reference ref-obj) (assert (array? ref-obj)) - (make-reference (map parse-literal (array->list ref-obj)))) + (make #:parts (map parse-literal (array->list ref-obj)))) (define (preprocess-policy ast) "Lift metadata and pre-process rules of `ast' to be more easily worked with in `parse-policy'" diff --git a/compiler/types.scm b/compiler/types.scm index eae55d7..2349082 100644 --- a/compiler/types.scm +++ b/compiler/types.scm @@ -16,219 +16,193 @@ (define-module (compiler types) #:use-module ((rnrs base) #:select (assert)) + #:use-module (compiler utilities) + #:use-module (ice-9 match) #:use-module (lens) + #:use-module (oop goops) #:use-module (srfi srfi-1) - #:use-module (srfi srfi-9) - #:export (make-policy - policy? - policy-package - policy-imports - policy-scope - policy-rules - policy-comments + #:use-module (srfi srfi-26) + #:export (policy-map + tree-map ;; TODO: Clean up these exports + + + rules ι-policy-scope ι-policy-rules - make-rule - rule? - rule-default - rule-head - rule-body + head + body ι-rule-body - make-head - head? - head-name - head-kind - head-lhs - head-rhs + + kind + left + right - make-body - body? - body-conjunction + inner ι-body-conjunction - make-reference - reference? - reference-parts - make-constant - constant? - constant-value - make-set-literal - set-literal? - set-literal-elems + - make-object-literal - object-literal? - object-literal-pairs - make-comprehension - comprehension? - comprehension-kind - comprehension-key - comprehension-value - comprehension-body - make-variable-ref - variable-ref? - variable-ref-name - make-scope - scope? - scope-identifiers + + identifiers scope-contains? - make-funcall - funcall? - funcall-ref - funcall-args + args - make-for-all - for-all? - for-all-domain - for-all-key - for-all-value - for-all-body - make-exists-some - - exists-some? - exists-some-identifiers)) + )) + +(define (policy-map policy f) + (over (ι-policy-rules) + (lambda (rules) + (zip (map car rules) + ;; n.b. discards `scope', `breadcrumb' + (map (compose (cut tree-map <> f (scope policy) '()) cadr) rules))) + policy)) + +(define-method (tree-map form f scope breadcrumb) + form) + +(define-method (tree-map (form ) f scope breadcrumb) + (f scope breadcrumb + (map (lambda (x i) + (let ((breadcrumb (add-to-end breadcrumb i))) + (f scope breadcrumb (tree-map x f scope breadcrumb)))) + form + (iota (length form))))) + + ;; Type representing an individual `.rego' file -(define-record-type - (make-policy package imports scope rules comments) - policy? - (package policy-package) - (imports policy-imports) - (scope policy-scope) - (rules policy-rules) - (comments policy-comments)) +(define-class () + (package #:init-value #f + #:init-keyword #:package) + (imports #:init-value #f + #:init-keyword #:imports) + (scope #:init-value #f + #:init-keyword #:scope + #:accessor scope) + (rules #:init-value #f + #:init-keyword #:rules + #:accessor rules) + (comments #:init-value #f + #:init-keyword #:comments)) (define (ι-policy-rules) "Lens for focusing the rules of a " - (lens policy-rules - (lambda (s f) - (make-policy - (policy-package s) - (policy-imports s) - (policy-scope s) - (f (policy-rules s)) - (policy-comments s))))) + (lens (lambda (s) (slot-ref s 'rules)) + (lambda (s f) (map-slot s 'rules f)))) (define (ι-policy-scope) "Lens for focusing the global of a " - (lens policy-rules - (lambda (s f) - (make-policy - (policy-package s) - (policy-imports s) - (f (policy-scope s)) - (policy-rules s) - (policy-comments s))))) + (lens (lambda (s) (slot-ref s 'scope)) + (lambda (s f) (map-slot s 'scope f)))) ;; The intensional database of a Rego program is defined by its rules. -(define-record-type - (make-rule default head body) - rule? - (default rule-default) - (head rule-head) - (body rule-body)) +(define-class () + (default #:init-form #f + #:init-keyword #:default) + (head #:init-keyword #:head + #:accessor head) + (body #:init-keyword #:body + #:accessor body)) (define (ι-rule-body) "Lens for focusing the body of a " - (lens rule-body - (lambda (s f) - (make-rule - (rule-default s) - (rule-head s) - (f (rule-body s)))))) + (lens (lambda (s) (slot-ref s 'body)) + (lambda (s f) (map-slot s 'body f)))) + +(define-method (tree-map (form ) f scope breadcrumb) + ;; TODO: Should this include the `head' of a rule too? + (match form + (($ default head body) + (let* ((breadcrumb (add-to-end breadcrumb (left head))) + (inner (with-modified-slot form 'body + (tree-map body f scope breadcrumb)))) + (f scope breadcrumb inner))))) ;; A (named) relation (equality or inclusion) that holds if the body of the rule is true. -(define-record-type - (make-head name kind lhs rhs) - head? - (name head-name) - (kind head-kind) ; memq '(value-eqv field-eqv set-memq) - (lhs head-lhs) - (rhs head-rhs)) +(define-class () + (name #:init-keyword #:name) + (kind #:init-keyword #:kind ; memq '(value-eqv field-eqv set-memq) + #:accessor kind) + (lhs #:init-keyword #:lhs + #:accessor left) + (rhs #:init-keyword #:rhs + #:accessor right)) ;; A conjunction that defines whether a rule should apply. -(define-record-type - (make-body conjunction) - body? - (conjunction body-conjunction)) +(define-class () + (conjunction #:init-keyword #:conjunction + #:accessor inner)) + +(define-method (tree-map (form ) f scope breadcrumb) + (match form + (($ conjunction) + (let ((inner (with-modified-slot form 'conjunction + (tree-map conjunction f scope breadcrumb)))) + (f scope breadcrumb inner))))) (define (ι-body-conjunction) "Lens for focusing the contents of a " - (lens body-conjunction - (lambda (s f) - (make-body - (f (body-conjunction s)))))) + (lens (lambda (s) (slot-ref s 'conjunction)) + (lambda (s f) (map-slot s 'conjunction f)))) ;; FIXME: Not inclusive of all literals. ;; Any literal occurrence in a Rego program. -(define-record-type - (make-constant value) - constant? - (value constant-value) ; scheme value - ) - -(define-record-type - (make-set-literal elems) - set-literal? - (elems set-literal-elems) ; list of [scheme value | | ] - ) - -(define-record-type - (make-object-literal pairs) - object-literal? - (pairs object-literal-pairs) ; list of [(string, scheme value | | )] - ) - -(define-record-type - (make-comprehension kind key value body) - comprehension? - (kind comprehension-kind) ; one of '(array set object) - (key comprehension-key) - (value comprehension-value) - (body comprehension-body) - ) - -(define-record-type - (make-variable-ref name) - variable-ref? - (name variable-ref-name) ; - ) - -(define-record-type - (make-invoke-membership elem set) - invoke-membership? - (elem invoke-membership-elem) ; - (set invoke-membership-set) ; - ) +(define-class () + (value #:init-keyword #:value)) ; scheme value -;; A place within a nested data structure. -(define-record-type - (make-reference parts) - reference? - (parts reference-parts)) +(define-class () + (elems #:init-keyword #:elems)) ; list of [scheme value | | ] + +(define-class () + (pairs #:init-keyword #:pairs)) ; list of [(string, scheme value | | )] + +(define-class () + (kind #:init-keyword #:kind) ; one of '(array set object) + (key #:init-keyword #:key) + (value #:init-keyword #:value) + (body #:init-keyword #:body)) -(define-record-type - (make-scope identifiers) - scope? - (identifiers scope-identifiers)) +(define-class () + (name #:init-keyword #:name)) ; + +(define-class () + (elem #:init-keyword #:elem) ; + (set #:init-keyword #:set)) ; + +;; A place within a nested data structure. +(define-class () + (parts #:init-keyword #:parts + #:accessor inner)) + +(define-method (tree-map (form ) f scope breadcrumb) + (match form + (($ parts) + (let ((inner (with-modified-slot form 'parts + (map (cut tree-map <> f scope breadcrumb) + parts)))) + (f scope breadcrumb inner))))) + +(define-class () + (identifiers #:init-keyword #:identifiers + #:accessor inner)) (define (scope-contains? scope breadcrumb identifier) "Whether SCOPE contains IDENTIFIER at the point indicated by BREADCRUMB @@ -236,33 +210,43 @@ `scope' is a , `breadcrumb' is a list given to the `f' of `mutate-tree', and `identifier' is a " (define (recur breadcrumb) - (or (member identifier (or (assoc-ref (scope-identifiers scope) breadcrumb) '())) + (or (member identifier (or (assoc-ref (inner scope) breadcrumb) '())) (if (null? breadcrumb) #t ;; (lean-builtin-function? (reference->lean-name identifier)) (recur (drop-right breadcrumb 1))))) - (assert (scope? scope)) - (assert (reference? identifier)) + (assert (is-a? scope )) + (assert (is-a? identifier )) (assert (list? breadcrumb)) (recur breadcrumb)) -(define-record-type - (make-funcall ref args) - funcall? - (ref funcall-name) ; (to the function being called) - (args funcall-args) ; list of - ) - -(define-record-type - (make-for-all domain key value body) - for-all? - (domain for-all-domain) - (key for-all-key) - (value for-all-value) - (body for-all-body) - ) - -(define-record-type - (make-exists-some identifiers) - exists-some? - (identifiers exists-some-identifiers)) +(define-class () + (ref #:init-keyword #:name) ; (to the function being called) + (args #:init-keyword #:args ; list of + #:accessor args)) + +(define-class () + (domain #:init-keyword #:domain) + (key #:init-keyword #:key) + (value #:init-keyword #:value) + (body #:init-keyword #:body)) + +(define-method (tree-map (form ) f scope breadcrumb) + (match form + (($ domain key value body) + (let ((inner (with-modified-slot form 'body + (map (cut tree-map <> f scope breadcrumb) + body)))) + (f scope breadcrumb inner))))) + +(define-class () + (identifiers #:init-keyword #:identifiers + #:accessor identifiers)) + +(define-method (tree-map (form ) f scope breadcrumb) + (match form + (($ identifiers) + (let ((inner (with-modified-slot form 'identifiers + (map (cut tree-map <> f scope breadcrumb) + identifiers)))) + (f scope breadcrumb inner))))) diff --git a/compiler/utilities.scm b/compiler/utilities.scm index 7591536..598d014 100644 --- a/compiler/utilities.scm +++ b/compiler/utilities.scm @@ -16,7 +16,9 @@ (define-module (compiler utilities) #:use-module (ice-9 match) + #:use-module (oop goops) #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) #:use-module (srfi srfi-31) #:use-module (srfi srfi-69) #:export (alist? @@ -29,7 +31,12 @@ hash-table-append-item! fresh-identifier - number->subscript)) + number->subscript + + map-slot + with-modified-slot + derive-display + to-pretty-sexp)) (define (alist? xs) "Return #t when XS is an alist" @@ -90,3 +97,42 @@ (#\8 #\₈) (#\9 #\₉)) (number->string n))) + +(define-syntax map-slot + (syntax-rules () + ((map-slot instance slot f) + (let ((clone (shallow-clone instance))) + (slot-set! clone slot (f (slot-ref clone slot))) + clone)))) + +(define-syntax with-modified-slot + (syntax-rules () + ((with-modified-slot instance slot new-value) + (let ((clone (shallow-clone instance))) + (slot-set! clone slot new-value) + clone)))) + +(define-syntax derive-display + (syntax-rules () + ((derive-display class) + (define-method (display (x class) port) + (format port "#<~a " (class-name class)) + (for-each (compose (lambda (y) + (display y port) + (display " ")) + (cut slot-ref x <>) + (cut slot-definition-name <>)) + (class-slots class)) + (display ">" port))))) + +(define (to-pretty-sexp form) + (cond ((hash-table? form) + (to-pretty-sexp (hash-table->alist form))) + ((instance? form) + `(,(class-name (class-of form)) + ,@(map (compose to-pretty-sexp + (cut slot-ref form <>) + (cut slot-definition-name <>)) + (class-slots (class-of form))))) + ((list? form) (map to-pretty-sexp form)) + (else form))) -- cgit v1.3