; This will be used on the server to validate all source code by the participants before running it
; Allowed procedures and special-forms will vary with each challenge

#lang racket

(require "challenge.rkt")
(require "either.rkt")
(require racket/format)

(provide validate run)

(define (validate str allowed)
  (call/cc
    (lambda (c)
      (call-with-exception-handler
        (lambda (e)
          (c (left (exn-message e))))
        (lambda ()
          (define expr (read (open-input-string str)))
          (check-allowed expr allowed))))))

(define (check-allowed expr allowed)
  (define (get-symbols expr)
    (if (pair? expr)
      (flatten (map get-symbols expr))
      (if (symbol? expr) (list expr) '())))
  (define syms (get-symbols expr))
  (define bad-syms (filter (lambda (s) (not (member s allowed))) syms))
  (if (null? bad-syms)
    (right expr)
    (left (apply string-append (cons "error: you used one or more procedures, special forms or variable names that has been disabled: " (map (lambda (s) (string-append " " s " ")) (map symbol->string bad-syms)))))))

; Allowing for functions with multiple arguments is something I leave for the future generation to implement
; It should be quite trivial add
(define (run challenge code)
  (call/cc
    (lambda (c)
      (call-with-exception-handler
        (lambda (e)
          (c (left (exn-message e))))
        (lambda ()
          (define input (challenge-input challenge))
          (define func (eval code (make-base-namespace)))
          (define res (map (lambda (in) (apply func (list in))) input))
          (if (equal? res (challenge-output challenge))
            (right "")
            (left res)))))))
