diff --git a/README.md b/README.md index 53d440d..6932eb1 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ order to conveniently generate characters and backstories for creative purposes. VillainHome) is Result)))) ;; we generate lots of story strings; we query the first such string below -(define solution-stream (all story-program)) +(define solution-stream (solve story-program)) > (get (stream-first solution-stream) 'story) "The heroic TA Zack from CT worked with Ari to thwart the wicked prof Ben of NY" @@ -120,4 +120,4 @@ Finally, you can run the tests with: ``` raco test -p minidusa -``` \ No newline at end of file +``` diff --git a/private/compile.rkt b/private/compile.rkt index fe1b99f..8eb289e 100644 --- a/private/compile.rkt +++ b/private/compile.rkt @@ -69,21 +69,25 @@ (rt:fact add1 (list (rt:variable 'X)) 2)))) '())) +;; An CompilerState is a +;; (compiler-state MutSymbolTable SymbolSet SymbolSet), tracking which relation +;; names are imported, external, and arities of the rest (internally defined) +(struct compiler-state [arities imports externs]) + ;; this is the old compile-time function -;; ImportsSyntax LogicSyntax -> RacketSyntax -(define (compile-logic imports-stx logic-stx) - (define rel-arities (local-symbol-table)) - (define imported-rel-vars (mutable-set)) - ; add all imported symbols; later we use this to compile attibutes - (for ([stx-pair (syntax->list imports-stx)]) - (syntax-parse stx-pair - [[rel-id _] - ; we want to put the symbols in our symbol table, not the identifiers, - ; otherwise we will not have the right notion of element equality - (set-add! imported-rel-vars (syntax->datum #'rel-id))])) +;; ImportsSyntax IdsSyntax LogicSyntax -> RacketSyntax +(define (compile-logic imports-stx externs-stx logic-stx) + (define arities (local-symbol-table)) + (define imports + (apply immutable-symbol-set + ; grabs all of the ([rel-var _] ...)s + (map (lambda (stx-pair) (first (syntax->list stx-pair))) + (syntax->list imports-stx)))) + (define externs (apply immutable-symbol-set (syntax->list externs-stx))) + (define state (compiler-state arities imports externs)) (define body - (let ([compile-decl ((curry compile-decl) rel-arities imported-rel-vars)] + (let ([compile-decl ((curry compile-decl) state)] [logic-stx (flatten-decls logic-stx)]) (syntax-parse logic-stx [(d ...) @@ -128,36 +132,36 @@ [[_ ((_ is? {_ ...+}) :- _ ...+)] #t] [_ #f])) -;; MutSymbolTable MutSymbolSet DeclSyntax -> [ListOf RacketSyntax] +;; CompilerState DeclSyntax -> [ListOf RacketSyntax] ;; This returns a list so that we can expand to multiple decls in the case of ;; a `decls` block. ;; Note that DeclSyntax _does_ include the expanded relation occurrence -(define (compile-decl arities imports decl-stx) +(define (compile-decl state decl-stx) ; partial application to thread around references to the symbol tables - (let ([compile-conc ((curry compile-conc) arities imports)] - [compile-decl ((curry compile-decl) arities imports)]) + (let ([compile-conc ((curry compile-conc) state)] + [compile-decl ((curry compile-decl) state)]) (syntax-parse decl-stx #:datum-literals (decls :-) [(decls d ...) (flatten (map compile-decl (attribute d)))] ;; get rid of the outer ref [[_ (conc :- prems ...+)] #:with (prem ...) - (map ((curry compile-prem) arities imports) (attribute prems)) + (map ((curry compile-prem) state) (attribute prems)) (list #`(rt:rule #,(compile-conc #'conc) (list prem ...)))] [[_ conc] (list #`(rt:rule #,(compile-conc #'conc) '()))]))) -;; MutSymbolTable MutSymbolSet ConclusionSyntax -> RacketSyntax +;; CompilerState ConclusionSyntax -> RacketSyntax ;; throws a compile-time error when there is a binding ;; occurrence of a variable in the conclusion, which is disallowed -(define (compile-conc arities imports conc-stx) +(define (compile-conc state conc-stx) ;; shadowing to disallow compiling terms with binding occurrences (let ([compile-term ((curry compile-term) #:forbid-binds "cannot bind variables in conclusions")] - [compile-rel-id ((curry compile-rel-id) arities imports)] + [compile-rel-id ((curry compile-rel-id) state)] [raise-if-imported! (lambda (name) - (when (set-member? imports (syntax->datum name)) + (when (symbol-set-member? (compiler-state-imports state) name) (raise-syntax-error #f "imported relations cannot appear in conclusions" @@ -180,26 +184,26 @@ (raise-if-imported! #'name) #'(rt:rule-frag rel-var-comped (list comp-t ...) '() #f)]))) -;; MutSymbolTable MutSymbolSet PremiseSyntax -> RacketSyntax -(define (compile-prem arities imports prem-stx) - (let ([compile-rel-id ((curry compile-rel-id) arities imports)] +;; CompilerState PremiseSyntax -> RacketSyntax +(define (compile-prem state prem-stx) + (let ([compile-rel-id ((curry compile-rel-id) state)] [compile-term-named (lambda (name) (curry compile-term #:forbid-binds - (and (set-member? imports name) + (and (symbol-set-member? (compiler-state-imports state) name) "cannot run imported relations backwards")))]) (syntax-parse prem-stx #:datum-literals (is) [((name t ...) is ch) #:with (comp-t ...) - (map (compile-term-named (syntax->datum #'name)) (attribute t)) + (map (compile-term-named #'name) (attribute t)) #:with rel-var-comped (compile-rel-id #'name (length (attribute t))) #`(rt:fact rel-var-comped (list comp-t ...) #,(compile-term #'ch))] [(name t ...) #:with (comp-t ...) (map compile-term (attribute t)) #:with rel-var-comped (compile-rel-id #'name (length (attribute t))) - (when (set-member? imports (syntax->datum #'name)) + (when (symbol-set-member? (compiler-state-imports state) #'name) (raise-syntax-error #f "imported relations must be used with 'is'" #'name)) @@ -209,7 +213,7 @@ ;; compiles to a reference to a bound procedure if it was imported; ;; otherwise checks the arity (if seen before, or sets arity otherwise) ;; and returns as the runtime representation of the name -(define (compile-rel-id arities imports rel-id arity) +(define (compile-rel-id state rel-id arity) (define rel-sym (syntax->datum rel-id)) (when (member rel-sym RESERVED-NAMES) @@ -222,12 +226,16 @@ (symbol-table-ref table key) (begin (symbol-table-set! table key val #:allow-overwrite? #t) val))) - - (if (set-member? imports rel-sym) - ; sets to arity if missing from the table -> will be equal - rel-id - (let ([expected-arity (symbol-table-ref! arities rel-id arity)]) - (unless (= arity expected-arity) + + (cond [(symbol-set-member? (compiler-state-imports state) rel-id) + rel-id] + [(symbol-set-member? (compiler-state-externs state) rel-id) + #`'#,rel-id] + [else + (define expected-arity + ; will add to the table if missing, which will pass equality check + (symbol-table-ref! (compiler-state-arities state) rel-id arity)) + (unless (= arity expected-arity) (raise-syntax-error #f (format @@ -236,7 +244,7 @@ expected-arity arity) rel-id)) - #`#'#,rel-id))) + #`#'#,rel-id])) ;; TermSyntax -> RacketSyntax (define (compile-term term-stx #:forbid-binds [message #f]) diff --git a/private/data.rkt b/private/data.rkt index 6ae1055..de2df28 100644 --- a/private/data.rkt +++ b/private/data.rkt @@ -31,6 +31,13 @@ ;; - Procedure, representing an imported relation ;; - Symbol, but only to allow users to interact with relations in facts +;; rel=?: Relation Relation -> Bool +(define (rel=? rel1 rel2) + (or (eq? rel1 rel2) ; for symbols and procedures + (and (syntax? rel1) + (syntax? rel2) + (bound-identifier=? rel1 rel2)))) + ;; A Fact is a (fact Relation [ListOf Datum] [Option Datum]). ;; It represents a known fact (either given or deduced) in the database. ;; Currently, stored facts will never store a procedure for the relation, @@ -43,10 +50,7 @@ [(define (equal-proc self other rec) ;; this first check is a hack, so that symbol comparison works in tests... ;; TODO: fix this by implementing a more proper equality check - (and (or (eq? (fact-rel self) (fact-rel other)) ; for proc + symbols - (and (syntax? (fact-rel self)) - (syntax? (fact-rel other)) - (bound-identifier=? (fact-rel self) (fact-rel other)))) + (and (rel=? (fact-rel self) (fact-rel other)) (rec (fact-terms self) (fact-terms other)) (rec (fact-value self) (fact-value other)))) (define (hash-proc self rec) @@ -64,17 +68,21 @@ ;; fact-stx-original?: Fact -> Bool ;; determines whether the fact's relation is syntax coming from a macro or not (define (fact-stx-original? f) - ;; strip off layer of indirection from syntax-spec - (define from (and (syntax? (fact-rel f)) - (syntax-property (fact-rel f) 'compiled-from))) - (and from (syntax-original? from))) + (define rel (fact-rel f)) + ;; strip off layer of indirection from syntax-spec, if it is a symbol + (define from-if-stx (and (syntax? rel) + (syntax-property rel 'compiled-from))) + (or (symbol? rel) + (and from-if-stx (syntax-original? from-if-stx)))) ;; smush-syntax/fact: Fact -> Fact ;; replaces the syntax object in a fact with its underlying symbol ;; (maybe the "signature" could be more descriptive, but this is clear imo) (define (smush-syntax/fact f) (define rel (fact-rel f)) - (struct-copy fact f [rel (if (procedure? rel) rel (syntax->datum rel))])) + (struct-copy fact f [rel (if (or (symbol? rel) (procedure? rel)) + rel + (syntax->datum rel))])) ;; A Variable is a (variable Symbol) (struct variable [name] #:transparent) @@ -92,7 +100,7 @@ (struct constraint [rel terms none-of] #:methods gen:equal+hash [(define (equal-proc self other rec) - (and (bound-identifier=? (constraint-rel self) (constraint-rel other)) + (and (rel=? (constraint-rel self) (constraint-rel other)) (rec (constraint-terms self) (constraint-terms other)) (rec (constraint-none-of self) (constraint-none-of other)))) (define (hash-proc self rec) @@ -104,13 +112,14 @@ (rec (constraint-terms self)) (rec (constraint-none-of self))))]) -;; A RuleFragment is (rule-frag Syntax [ListOf Term] [ListOf Term] Boolean) +;; A RuleFragment is (rule-frag Relation [ListOf Term] [ListOf Term] Boolean) ;; It is an _internal representation_ of the source syntax, which in ;; general may contain variables. These can be combined to form rules ;; or facts (closed rules with no premises). ;; Notably, a Term is an immediate, and does not have nested terms: ;; this is a difference from the surface syntax; these nested ;; terms will be desugared in an ANF-like pass. +;; The Relation here will not be a Procedure, currently. ;; A ClosedRuleFragment is ;; (rule-frag Syntax [ListOf Datum] [ListOf Datum] [OneOf Boolean 'tried]) @@ -121,11 +130,7 @@ (struct rule-frag [name terms choices is??] #:transparent #:methods gen:equal+hash [(define (equal-proc self other rec) - (and (or (eq? (rule-frag-name self) (rule-frag-name other)) - (and (syntax? (rule-frag-name self)) - (syntax? (rule-frag-name other)) - (bound-identifier=? (rule-frag-name self) - (rule-frag-name other)))) + (and (rel=? (rule-frag-name self) (rule-frag-name other)) (rec (rule-frag-terms self) (rule-frag-terms other)) (rec (rule-frag-choices self) (rule-frag-choices other)) (rec (rule-frag-is?? self) (rule-frag-is?? other)))) diff --git a/private/database.rkt b/private/database.rkt index 249a919..7c7dc91 100644 --- a/private/database.rkt +++ b/private/database.rkt @@ -18,6 +18,10 @@ (define (db-of . facts) (database (apply set facts) (set))) +;; factset->db: [SetOf Fact] -> Database +(define (factset->db factset) + (database factset (set))) + ;; db-empty? : Database -> Boolean ;; Returns #f if the database contains facts. (define (db-empty? db) @@ -43,8 +47,7 @@ (for/set ([c (database-constraints db)] #:when (not (and (equal? (fact-terms f) (constraint-terms c)) - (bound-identifier=? (fact-rel f) - (constraint-rel c))))) + (rel=? (fact-rel f) (constraint-rel c))))) c))) ;; db-add-constraint : Constraint Database -> Database @@ -74,7 +77,7 @@ ;; Determine if the given fact has the same attribute as ;; rel and terms. (define (attr-like? f) - (and (bound-identifier=? (fact-rel f) rel) + (and (rel=? (fact-rel f) rel) (equal? (fact-terms f) terms))) (not (db-empty? (db-filter attr-like? db)))) @@ -102,14 +105,14 @@ ;; different values. ;; This should only be called when to-add is a Fact. (define (fact-consistent? known) - (not (and (bound-identifier=? (fact-rel to-add) (fact-rel known)) + (not (and (rel=? (fact-rel to-add) (fact-rel known)) (equal? (fact-terms to-add) (fact-terms known)) (not (equal? (fact-value to-add) (fact-value known)))))) ;; constraint-valid? : Constraint Fact -> Boolean ;; Determine if the fact is consistent given the constraint c. (define (constraint-valid? c f) - (not (and (bound-identifier=? (fact-rel f) (constraint-rel c)) + (not (and (rel=? (fact-rel f) (constraint-rel c)) (equal? (fact-terms f) (constraint-terms c)) (set-member? (constraint-none-of c) (fact-value f))))) diff --git a/private/runtime.rkt b/private/runtime.rkt index 17a6f1f..80bb142 100644 --- a/private/runtime.rkt +++ b/private/runtime.rkt @@ -1,13 +1,14 @@ #lang racket -(provide all +(provide (rename-out [solve-opt solve]) has get soln->factset) (require racket/stream "data.rkt" - "database.rkt") + "database.rkt" + (for-syntax syntax/parse)) ;; we have some database (D) of known facts ;; we want to evolve this database @@ -61,11 +62,11 @@ ;; sample : Logic -> [Maybe Solution] ;; Obtain one possible solution of the given program, if one exists. -;; all : Logic -> [StreamOf Solution] +;; all : Logic [SetOf Fact] -> [StreamOf Solution] ;; Obtain a stream of all possible solutions of the given program. ;; The stream may be infinite, and computing the next item may not ;; always terminate. -(define (all prog) +(define (solve-entry prog facts) ;; result->stream : SolutionResult -> [StreamOf Solution] ;; Processes a SolutionResult into a stream of Solution by recursively ;; backtracking through all possible intermediate choices. @@ -78,7 +79,17 @@ (define (collect-backtracked stack) (result->stream (backtrack prog stack))) - (result->stream (solve prog (db-of) '()))) + (result->stream (solve prog (factset->db facts) '()))) + +;; version of all that takes facts as an optional keyword argument +(define-syntax solve-opt + (lambda (stx) + (syntax-parse stx + [(_ (~or* (~seq #:facts factset) + (~seq)) + prog) + #:with facts (or (attribute factset) #'(set)) + #'(solve-entry prog facts)]))) ;; solve : Logic Database SearchStack -> SolutionResult ;; Given a search state and a database of currently known facts, obtain a @@ -214,7 +225,7 @@ ; 'tried = #t for our purposes (define (conclusion=? a b) - (and (bound-identifier=? (rule-frag-name a) (rule-frag-name b)) + (and (rel=? (rule-frag-name a) (rule-frag-name b)) (equal? (rule-frag-terms a) (rule-frag-terms b)) (equal? (rule-frag-choices a) (rule-frag-choices b)) (equal? (not (rule-frag-is?? a)) (not (rule-frag-is?? b))))) @@ -299,7 +310,7 @@ ;; looks-like? : Fact -> Boolean (define (looks-like? fact) - (and (bound-identifier=? (fact-rel open) (fact-rel fact)) + (and (rel=? (fact-rel open) (fact-rel fact)) (andmap similar? (fact-terms open) (fact-terms fact)) @@ -409,7 +420,7 @@ (and (or (eq? (fact-rel f) rel) ; for proc + symbols (and (syntax? (fact-rel f)) (syntax? rel) - (bound-identifier=? (fact-rel f) rel))) + (rel=? (fact-rel f) rel))) (equal? (fact-terms f) terms))) ;; has: Solution Symbol Datum ... -> Bool diff --git a/private/spec.rkt b/private/spec.rkt index 19b767f..8bbe0cd 100644 --- a/private/spec.rkt +++ b/private/spec.rkt @@ -18,11 +18,11 @@ (binding-class rel-var) (extension-class logic-macro #:binding-space minidusa) - ;; (logic/importing ...) + ;; (logic/importing ( ...) ...) (host-interface/expression - (logic/importing i:imps d:decl ...) - #:binding (nest i (scope (import d) ...)) - (compile-logic #'i #'(d ...))) + (logic/importing i:imps (ex:rel-var ...) d:decl ...) + #:binding (nest i (scope (bind ex) ... (import d) ...)) + (compile-logic #'i #'(ex ...) #'(d ...))) ;; ::= ( ...) ;; ::= x:racket-var @@ -126,12 +126,16 @@ ) ;; logic : (logic ...) -;; | (logic #:import [ ...] ...) +;; | (logic #:import [ ...] #:extern [id ...] ...) (define-syntax logic (lambda (stx) (syntax-parse stx [(_ (~or* (~seq #:import imports) (~seq)) + ;; TODO: is there a way to make it so order doesn't matter? + (~or* (~seq #:extern externs) + (~seq)) ds ...) #:with imps (or (attribute imports) #'()) - #'(logic/importing imps ds ...)]))) + #:with exts (or (attribute externs) #'()) + #'(logic/importing imps exts ds ...)]))) diff --git a/scribblings/minidusa.scrbl b/scribblings/minidusa.scrbl index 7d78647..7efbbcc 100644 --- a/scribblings/minidusa.scrbl +++ b/scribblings/minidusa.scrbl @@ -175,7 +175,7 @@ characters and backstories for creative purposes. " of " VillainHome) is Result)))) - (define solution-stream (all story-program)) + (define solution-stream (solve story-program)) (get (stream-first solution-stream) 'story)] @@ -247,7 +247,8 @@ supported. Determines whether the argument is @code{NONE}. } -@defproc[(all [program program?]) (stream? solution?)]{ +@; TODO: this actually takes in an optional keyword argument too, and is a macro +@defproc[(solve [program program?]) (stream? solution?)]{ Obtain a stream of all possible solutions of the given program that is defined via a @code{logic} macro. The stream may be infinite, and computing the next item may not always terminate. diff --git a/testing.rkt b/testing.rkt index c4ac1a0..22eb516 100644 --- a/testing.rkt +++ b/testing.rkt @@ -29,5 +29,5 @@ ;; check-all-solutions: Program [ListOf [SetOf Fact]] -> Void (define-syntax-rule (check-all-solutions prog expected) - (check-equal? (map soln->factset (stream->list (all prog))) + (check-equal? (map soln->factset (stream->list (solve prog))) expected)) \ No newline at end of file diff --git a/tests/extensions.rkt b/tests/extensions.rkt index 95e9e34..fc7ba22 100644 --- a/tests/extensions.rkt +++ b/tests/extensions.rkt @@ -6,20 +6,20 @@ syntax-spec-v3) (check-equal? - (length (stream->list (all (logic - (edge 'a 'b) - (edge 'b 'c) - (edge 'a 'c) - (edge 'c 'd) - (edge 'a 'e) - ((edge X Y) :- (edge Y X)) - ((node X) :- (edge X _)) - (((color X) is {1 2 3}) :- (node X)) - - ((ok) is {#t}) - (((ok) is {#f}) :- (edge X Y) - ((color X) is C) - ((color Y) is C)))))) + (length (stream->list (solve (logic + (edge 'a 'b) + (edge 'b 'c) + (edge 'a 'c) + (edge 'c 'd) + (edge 'a 'e) + ((edge X Y) :- (edge Y X)) + ((node X) :- (edge X _)) + (((color X) is {1 2 3}) :- (node X)) + + ((ok) is {#t}) + (((ok) is {#f}) :- (edge X Y) + ((color X) is C) + ((color Y) is C)))))) 24) (define-dsl-syntax graph logic-macro @@ -49,17 +49,17 @@ ((edge X Y) :- (edge Y X))) (check-equal? - (length (stream->list (all (logic - (graph edge - ('a ['b 'c 'e]) - ('c ['b 'd])) - ((node X) :- (edge X _)) - (((color X) is {1 2 3}) :- (node X)) - - ((ok) is {#t}) - (((ok) is {#f}) :- (edge X Y) - ((color X) is C) - ((color Y) is C)))))) + (length (stream->list (solve (logic + (graph edge + ('a ['b 'c 'e]) + ('c ['b 'd])) + ((node X) :- (edge X _)) + (((color X) is {1 2 3}) :- (node X)) + + ((ok) is {#t}) + (((ok) is {#f}) :- (edge X Y) + ((color X) is C) + ((color Y) is C)))))) 24) (define-dsl-syntax forbid logic-macro @@ -70,17 +70,17 @@ (((name) is {#f}) :- p ...))]))) (check-equal? - (length (stream->list (all (logic - (graph edge - ('a ['b 'c 'e]) - ('c ['b 'd])) - ((node X) :- (edge X _)) - (((color X) is {1 2 3}) :- (node X)) - - (forbid ok - (edge X Y) - ((color X) is C) - ((color Y) is C)))))) + (length (stream->list (solve (logic + (graph edge + ('a ['b 'c 'e]) + ('c ['b 'd])) + ((node X) :- (edge X _)) + (((color X) is {1 2 3}) :- (node X)) + + (forbid ok + (edge X Y) + ((color X) is C) + ((color Y) is C)))))) 24) (check-all-solutions @@ -116,17 +116,25 @@ (fact 'ok '() #t) (fact 'd '() #t)))) - (define-dsl-syntax mydecl logic-macro + (define-dsl-syntax deduce-name logic-macro (lambda (stx) (syntax-parse stx [(_ name) #'(decls (foo) ((name) :- (foo)))]))) + (define-dsl-syntax has-foo-1 logic-macro + (lambda (stx) + (syntax-parse stx + [(_) + #'(foo 1)]))) + (check-all-solutions (logic - (mydecl bar) ;; tests that the arities do not conflict, that the fresh symbol foo does ;; not appear in the solution, but bar (deduced using foo) DOES appear + (deduce-name bar) + ;; this will again test arity, and that foo 2 is not deduced + (has-foo-1) ((foo 2) :- (foo 1))) (list (set (fact 'bar '()))))) diff --git a/tests/runtime.rkt b/tests/runtime.rkt index 132fe6f..2fb268f 100644 --- a/tests/runtime.rkt +++ b/tests/runtime.rkt @@ -4,44 +4,47 @@ (require "../testing.rkt") (define simple-program - (logic ((foo 1) is {'a}))) + (program + (list (rule (rule-frag 'foo '(1) '(a) #f) '())) + '())) (check-equal? - (has (stream-first (all simple-program)) 'foo 1) + (has (stream-first (solve simple-program)) 'foo 1) #t) (check-equal? - (has (stream-first (all simple-program)) 'foo 2) + (has (stream-first (solve simple-program)) 'foo 2) #f) (check-equal? - (get (stream-first (all simple-program)) 'foo 1) + (get (stream-first (solve simple-program)) 'foo 1) 'a) (check-exn ;; TODO: make this error message better #rx"" - (lambda () (get (stream-first (all simple-program)) 'foo 2))) + (lambda () (get (stream-first (solve simple-program)) 'foo 2))) ;; we can run imported relations (check-equal? - (stream->list (all (program - (list (rule (rule-frag 'foo '() '() #f) - (list (fact add1 '(0) 1)))) - '()))) + (stream->list (solve (program + (list (rule (rule-frag 'foo '() '() #f) + (list (fact add1 '(0) 1)))) + '()))) (list (solution (db-of (make-fact 'foo '()))))) (check-equal? - (stream->list (all (program - (list (rule (rule-frag 'foo '() '() #f) - (list (fact * '(0 1 2) 1)))) - '()))) + (stream->list (solve (program + (list (rule (rule-frag 'foo '() '() #f) + (list (fact * '(0 1 2) 1)))) + '()))) (list (solution (db-of)))) ;; example where we bind a variable based on an import (check-equal? - (stream->list (all (program - (list (rule (rule-frag 'foo '() (list (variable 'X)) #f) - (list (fact + '(1 2 3 4) (variable 'X))))) - '()))) + (stream->list + (solve (program + (list (rule (rule-frag 'foo '() (list (variable 'X)) #f) + (list (fact + '(1 2 3 4) (variable 'X))))) + '()))) (list (solution (db-of (make-fact 'foo '() 10)))))) \ No newline at end of file diff --git a/tests/test.rkt b/tests/test.rkt index 75522be..3f2047c 100644 --- a/tests/test.rkt +++ b/tests/test.rkt @@ -85,43 +85,43 @@ (check-equal? (length (stream->list - (all (logic - (edge 'a 'b) - (edge 'b 'c) - ((edge X Y) :- (edge Y X)) - ((node X) :- (edge X _)) - (((color X) is {1 2 3}) :- (node X)))))) + (solve (logic + (edge 'a 'b) + (edge 'b 'c) + ((edge X Y) :- (edge Y X)) + ((node X) :- (edge X _)) + (((color X) is {1 2 3}) :- (node X)))))) 27) (check-equal? - (length (stream->list (all (logic - (edge 'a 'b) - (edge 'b 'c) - ((edge X Y) :- (edge Y X)) - ((node X) :- (edge X _)) - (((color X) is {1 2 3}) :- (node X)) - - ((ok) is {#t}) - (((ok) is {#f}) :- (edge X Y) - ((color X) is C) - ((color Y) is C)))))) + (length (stream->list (solve (logic + (edge 'a 'b) + (edge 'b 'c) + ((edge X Y) :- (edge Y X)) + ((node X) :- (edge X _)) + (((color X) is {1 2 3}) :- (node X)) + + ((ok) is {#t}) + (((ok) is {#f}) :- (edge X Y) + ((color X) is C) + ((color Y) is C)))))) 12) (check-equal? - (length (stream->list (all (logic - (edge 'a 'b) - (edge 'b 'c) - (edge 'a 'c) - (edge 'c 'd) - (edge 'a 'e) - ((edge X Y) :- (edge Y X)) - ((node X) :- (edge X _)) - (((color X) is {1 2 3}) :- (node X)) - - ((ok) is {#t}) - (((ok) is {#f}) :- (edge X Y) - ((color X) is C) - ((color Y) is C)))))) + (length (stream->list (solve (logic + (edge 'a 'b) + (edge 'b 'c) + (edge 'a 'c) + (edge 'c 'd) + (edge 'a 'e) + ((edge X Y) :- (edge Y X)) + ((node X) :- (edge X _)) + (((color X) is {1 2 3}) :- (node X)) + + ((ok) is {#t}) + (((ok) is {#f}) :- (edge X Y) + ((color X) is C) + ((color Y) is C)))))) 24) ;; - Alice @@ -165,7 +165,7 @@ (fact 'bar '(6))))) (check-equal? - (length (stream->list (all + (length (stream->list (solve (logic #:import ([s add1]) ((run 0) is {'stop 'go}) (((run M) is {'stop 'go}) @@ -217,13 +217,13 @@ (check-equal? (length (stream->list - (all (logic - ((a 1 2 3) is {#t #f}) - ((b) is {#t #f}) - ((b) is {#t #f}) - ((c) is {#t #f}) - (bar 1) ; maybe this should be disallowed... - ((bar 2) is {#t #t}))))) + (solve (logic + ((a 1 2 3) is {#t #f}) + ((b) is {#t #f}) + ((b) is {#t #f}) + ((c) is {#t #f}) + (bar 1) ; maybe this should be disallowed... + ((bar 2) is {#t #t}))))) 16) (check-all-solutions @@ -240,4 +240,21 @@ (fact 'b '())) (set (fact 'a '() 2) (fact 'b '())))) + + ;; TODO: make sure that this plays well with hygiene + (define extern-prog + (logic #:extern (foo bar) + (foo 1) + ((bar 2) :- (foo 2)))) + + (check-equal? + (soln->factset (stream-first (solve extern-prog))) + (set (fact 'foo '(1)))) + + (check-equal? + (soln->factset + (stream-first (solve #:facts (set (fact 'foo '(2))) extern-prog))) + (set (fact 'foo '(1)) + (fact 'foo '(2)) + (fact 'bar '(2)))) )