#cs(module abs mzscheme

  ;; Data Definition for Abstract Syntax Trees

  ;: Exp := (make-bool-exp boolean) | (make-null-exp) | (make-num-exp number)
  ;;      | (make-id-exp symbol) | (make-prim-exp symbol) 
  ;;      | (make-uop-exp symbol Exp) | (make-biop-exp symbol Exp Exp) | 
  ;;      | (make-map-exp (list-of symbol) Exp) | (make-app-exp Exp (list-of Exp)) 
  ;;      | (make-if-exp Exp Exp Exp) | (make-let-exp (list-of id-defn) Exp) |
  ;;      | (make-block-exp (list-of Exp))  ;; not used in Assignment 1 !

  ;; id-defn := (make-id-defn symbol Exp)

  (provide 
  	   (struct bool-exp (arg))                 ; arg: {'true,'false}
           (struct null-exp ())
    	   (struct num-exp (arg))                  ; arg: number 
           (struct id-exp (arg))                   ; arg: symbol
           (struct prim-exp (arg))                 ; arg: symbol
           (struct uop-exp (rator rand))           ; rator: symbol ; rand: Exp
           (struct biop-exp (rator rand1 rand2))   ; rator: symbol ; rand1, rand2: Exp
           (struct map-exp (vars body))            ; vars: list of symbol  ; body: Exp
           (struct app-exp (rator l-of-rands))     ; rator: Exp  ; l-of-rands: list of exp
           (struct if-exp (test conseq alt))       ; test, conseq, alt: Exp
           (struct let-exp (defns body))           ; defns: list of id-defn  ; body: Exp 
           (struct id-defn (id body))              ; id: symbol  ; body: Exp

           (struct block-exp (l-of-exps))          ; l-of-exps: non-empty list of expressions 
           ;; block-exp will be used later in the course, but not in Assignment 1
	   deconvert
	   tostring
  )

  (define-struct bool-exp (arg) (make-inspector))
    ; arg: {'true,'false}

  (define-struct null-exp () (make-inspector))
    
  (define-struct num-exp (arg) (make-inspector))
    ; arg: number 

  (define-struct id-exp (arg) (make-inspector))
    ; arg: symbol

  (define-struct prim-exp (arg) (make-inspector))
    ; arg: symbol

  (define-struct uop-exp (rator rand) (make-inspector))
    ; rator: symbol
    ; rand: Exp

  (define-struct biop-exp (rator rand1 rand2) (make-inspector))
    ; rator: symbol 
    ; rand1, rand2: Exp

  (define-struct map-exp (vars body) (make-inspector))
    ; vars: list of symbol
    ; body: Exp

  (define-struct app-exp (rator l-of-rands) (make-inspector))
    ; rator: Exp  
    ; l-of-rands: list of exp
  
  (define-struct if-exp (test conseq alt) (make-inspector))
    ; test, conseq, alt: Exp
  
  (define-struct let-exp (defns body) (make-inspector))
    ; defns: list of id-defn
    ; body: Exp 

  (define-struct id-defn (id body) (make-inspector))
    ; id: symbol
    ; body: Exp

  (define-struct block-exp (l-of-exps) (make-inspector))  ;; not used yet!
    ; l-of-exps: non-empty list of expressions


  ;; Data Definition for Lexer

  ;; Token := number | (make-delimiter symbol) | (make-operator symbol) 
  ;;        | (make-primitive-token symbol) | (make-keyword symbol) 
  ;;        | (make-variable symbol)


  (provide 
  	   (struct delimiter (sym))   ; sym: symbol
           (struct operator (sym))    ; sym: symbol

           (struct primitive-token (sym))   ; sym: symbol

           (struct keyword (sym))     ; sym: symbol
           (struct variable (sym))    ; sym: symbol
  )

  (define-struct delimiter (sym) (make-inspector))
  ; sym: symbol

  (define-struct operator (sym) (make-inspector))
  ; sym: symbol

  (define-struct primitive-token (sym) (make-inspector))  ; token is the name of a primitive procedure
  ; sym: symbol

  (define-struct keyword (sym) (make-inspector))
  ; sym: symbol
  
  (define-struct variable (sym) (make-inspector))
  ; sym: symbol


  ;; deconverter for abstract syntax trees

  ;; Exp -> S-expression
  ;; (deconvert e) converts the Jam expression e to a more readable list representation
  (define deconvert
    (lambda (at)
      (cond
	[(id-exp? at) (id-exp-arg at)]
	[(bool-exp? at) (bool-exp-arg at)]
	[(num-exp? at) (num-exp-arg at)]
	[(prim-exp? at) (prim-exp-arg at)]
	[(null-exp? at) 'null]
	[(uop-exp? at) (list (uop-exp-rator at) (deconvert (uop-exp-rand at)))]
	[(biop-exp? at) (list (deconvert (biop-exp-rand1 at))
			      (biop-exp-rator at)
			      (deconvert(biop-exp-rand2 at)))]
	[(app-exp? at) (cons (deconvert (app-exp-rator at))
			     (map deconvert (app-exp-l-of-rands at)))]
	[(if-exp? at)
	 (list 'if (deconvert (if-exp-test at)) 'then (deconvert (if-exp-conseq at)) 'else
	       (deconvert (if-exp-alt at)))]
	[(let-exp? at)
	 (list 'let (defns-deconvert (let-exp-defns at)) 'in (deconvert (let-exp-body at)))]
	[(map-exp? at)
	 (list 'map (map-exp-vars at) 'to (deconvert (map-exp-body at)))]
	[else (error 'deconvert "unrecognized abstract syntax tree: ~a" at)])))
  

  ;; (list-of id-defn) -> (list-of S-expression)
  ;; (defns-deconvert defns) converts the list of Jam id-defns to a more readable list representation
  (define defns-deconvert
    (lambda (defns)
      (map defn-deconvert defns)))
  
  ;; id-defn -> S-expression
  ;; (defn-deconvert def) converts the Jam id-defn to a more readable list representation
  (define defn-deconvert
    (lambda (defn)
      (list (id-defn-id defn) ':= (deconvert (id-defn-body defn)))))
  
  

  ;;tostring stuff.. 
  ;;should refactor this stuff
  (define tos symbol->string)
  (define sa string-append)
  
  (define los 
    (lambda (list)
      (if (null? list) ""
	  (sa (car list) (los (cdr list))))))
  
  (define los2 
    (lambda (list pre in post)
      (letrec [(helper (lambda (list pre in) 
			 (if (null? list)  ""
			     (sa pre (car list) 
				 (helper (cdr list) in in)))))]
	(sa pre (helper list "" in) post))))
  
  
  (define is-term?
    (lambda (x) 
      (or (uop-exp? x)
	  (app-exp? x)
	  (null-exp? x)
	  (num-exp? x)
	  (bool-exp? x))))
  
  (define is-exp?
    (lambda (x)
      (or (is-term? x)
	  (biop-exp? x)
	  (if-exp? x)
	  (let-exp? x)
	  (map-exp? x))))
  
  ;; Exp -> S-expression
  ;; (tostring e) converts the Jam expression e to a more readable string representation
  (define tostring
    (lambda (at)
      (cond 
       [(id-exp? at) (tos (id-exp-arg at))]
       [(bool-exp? at) (tos (bool-exp-arg at))]
       [(num-exp? at) (number->string (num-exp-arg at))]
       [(prim-exp? at) (tos (prim-exp-arg at))]
       [(null-exp? at) (tos 'null)]
       [(uop-exp? at) (sa (tos (uop-exp-rator at)) " " (tostring (uop-exp-rand at)))]
       [(biop-exp? at) (sa "(" (tostring (biop-exp-rand1 at)) 
			   " " (tos (biop-exp-rator at) ) " "
			   (tostring (biop-exp-rand2 at)) ")")]
       [(app-exp? at) (sa 
		       (let ([rator (app-exp-rator at)])
			 (if (is-exp? rator) 
			     (sa "(" (tostring rator) ")") 
			     (tostring rator)))
		       (los2 (map tostring (app-exp-l-of-rands at)) "(" ", " ")"))]
       [(if-exp? at) 
	(sa "if " (tostring (if-exp-test at)) " then " (tostring (if-exp-conseq at)) " else "
	    (tostring (if-exp-alt at)))]
       [(let-exp? at)
	(sa "let " (defns-tostring (let-exp-defns at)) "in " (tostring (let-exp-body at)))]
       [(map-exp? at)
	(sa "map " (los2 (map tos (map-exp-vars at)) "" "," "") " " (tos 'to) " " (tostring (map-exp-body at)))]
       [else (error 'tostring "unrecognized abstract syntax tree: ~a" at)])))
  
  ;; (list-of id-defn) -> (list-of S-expression)
  ;; (defns-deconvert defns) converts the list of Jam id-defns to a more readable list representation
  (define defns-tostring
    (lambda (defns)
      (los (map defn-tostring defns))))
  
  ;; id-defn -> S-expression
  ;; (defn-deconvert def) converts the Jam id-defn to a more readable list representation
  (define defn-tostring
    (lambda (defn)
      (sa (tos (id-defn-id defn)) " := " (tostring (id-defn-body defn)) "; ")))
  
)
