#lang mzscheme

(require mzlib/defmacro)


;;; remember that if you want to require something that works with macros, you need to require-for-syntax it
;;; for instance:
;;; (require-for-syntax srfi/1)

(define-macro (define+ . defs)
  `(begin
     (define ,@defs)
     (define-for-syntax ,@defs)))

;;; use expand1 to debug your macros
;;; for instance, (expand1 '(arith 1 + 1)) will return at first (display "implement me") and later (after implementation) it will return (+ 1 1)

(define (expand1 stx) (syntax-object->datum (expand-once stx)))



;;; we want to define an arithmetic macro
;;; basically, in the middle of some code, i want to be like
;;; (arith 1 + x * (foo x))
;;; and have it expand into 
;;; (+ 1 (* x (foo x)))
;;; (notice this does order of operations, but leaves the function calls in tact
;;;
;;; note that it doesnt really have to do parens for order of ops, since if given
;;; (a * (b + c)) it doesn't know whether that means (* a (+ b c)) or (* a (b + c)) where 
;;; b is a function being called with arguments a and c
;;; however, the arith macro implicetly does parens.  
(define-macro (arith . expression)
  `(display "implement me"))

;;; test it with this
(define (quadratic-formula a b c)
  (arith (arith - a + (sqrt (arith (square b) - 4 * a * c))) / (arith 2 * a)))



;;; here is an example of a macro which just repeats its body n times
(define-macro (repeat-n-times n . body)
  (let ((iterat-fun (gensym))
        (it-var (gensym))
        (N (gensym)))
    `(let ((,N ,n))
       (letrec ((,iterat-fun (lambda (,it-var)
                               (if (= ,it-var ,N)
                                   (void)
                                   (begin
                                     ,@body
                                     (,iterat-fun (+ ,it-var 1)))))))
         (,iterat-fun 0)))))
  
(define (say-hi-n-times n)
  (repeat-n-times n
     (display "hey")
     (newline)))


;;; how about adding a while loop macro
;;; I want to write programs like this
;;; (let ((i 10))
;;;   (while (< i 0)
;;;      (set! i (- i 1))
;;;      (display i)
;;;      (newline)))

;;; the while part could expand into, for instance
;;; (letrec ((g652 (lambda ()
;;;                  (if (> i 0)
;;;                      (begin
;;;                        (set! i (- i 1))
;;;                        (display i)
;;;                        (newline)
;;;                        (g652))
;;;                      (void)))))
;;;   (g652))

;;; perhaps you can find some other way to do it.
;;; remember, anything in a scope where the while is should be in scope when the while expands
;;; also, make sure the relevent code does not excecute until it should, and you'll need to add begins

(define-macro (while condition . body)
  `(display "implement me"))


;;; think about adding a for macro, so you could write programs like this
;;; (for (i 0) (< i 10) (+ i 1)
;;;    (display i)
;;;    (newline))

;;; this could expand into
;;; (let ((i 0))
;;;   (while (< i 10)
;;;     (display i)
;;;     (newline)
;;;     (set! i (+ i 1))))


;;; it could also expand into different sorts of letrecs or function calls, the choice is yours
(define-macro (for var-dec condition next . body)
  (display "implement me"))



