#lang racket/base
(require racket/contract
         racket/match
         datalog/ast
         datalog/stx)
(require (for-template datalog/stx))

(provide
 current-datalog-introducer
 (contract-out
  [compile-program (program/c . -> . (listof syntax?))]
  [compile-statement (statement/c . -> . syntax?)]))

(define (compile-program p)
  (map compile-statement p))

(define current-datalog-introducer
  (make-parameter (λ (x) x)))

(define (intro x)
  ((current-datalog-introducer) x))

(define compile-statement
  (match-lambda
    [(assertion srcloc c)
     (define srcstx (datum->syntax #f 'x srcloc))
     (quasisyntax/loc srcstx
       (#,(intro #'!) #,(compile-clause c)))]
    [(retraction srcloc c)
     (define srcstx (datum->syntax #f 'x srcloc))
     (quasisyntax/loc srcstx
       (#,(intro #'~) #,(compile-clause c)))]
    [(query srcloc l)
     (define srcstx (datum->syntax #f 'x srcloc))
     (quasisyntax/loc srcstx
       (#,(intro #'?) #,(compile-literal l)))]
    [(requirement srcloc l)
     (define srcstx (datum->syntax #f 'x srcloc))
     (quasisyntax/loc srcstx
       (#,(intro #'require) #,(datum->syntax #f (string->symbol l))))]))

(define compile-clause
  (match-lambda
    [(clause srcloc head (list))
     (define srcstx (datum->syntax #f 'x srcloc))
     (compile-literal head)]
    [(clause srcloc head body)
     (define srcstx (datum->syntax #f 'x srcloc))
     (quasisyntax/loc srcstx
       (#,(intro #':-) #,@(map compile-literal (list* head body))))]))

(define compile-literal
  (match-lambda
    [(literal srcloc '= (and ts (app length 2)))
     (define srcstx (datum->syntax #f 'x srcloc))
     (quasisyntax/loc srcstx
       (#,(intro #'=) #,@(map compile-term ts)))]
    [(literal srcloc pred ts)
     (define srcstx (datum->syntax #f 'x srcloc))
     (define pred-stx (if (predicate-sym? pred)
                          (sym->original-syntax (predicate-sym-sym pred)
                                                (predicate-sym-srcloc pred))
                          pred))
     (quasisyntax/loc srcstx
       (#,pred-stx #,@(map compile-term ts)))]
    [(external srcloc predicate-sym predicate arg-terms ans-terms)
     (define srcstx (datum->syntax #f 'x srcloc))
     (quasisyntax/loc srcstx
       (#,(datum->syntax #f predicate-sym)
        #,@(map compile-term arg-terms)
        #,(intro #':-)
        #,@(map compile-term ans-terms)))]))

(define compile-term
  (match-lambda
    [(variable srcloc sym)
     (sym->original-syntax sym srcloc)]
    [(constant srcloc cnst)
     (sym->original-syntax cnst srcloc)]))

(define (sym->original-syntax cnst srcloc)
  (define p (open-input-string (format "~s" cnst)))
  (port-count-lines! p)
  (match-define (list source-name line column position span) srcloc)
  (set-port-next-location! p line column position)
  (syntax-property (read-syntax source-name p) 'original-for-check-syntax #t))
