;; nyacc/lang/c99/c99-01.test		-*- scheme -*-
;;
;; Copyright (C) 2017 Matthew R. Wette
;; 
;; Copying and distribution of this file, with or without modification,
;; are permitted in any medium without royalty provided the copyright
;; notice and this notice are preserved.  This file is offered as-is,
;; without any warranty.

;; CPP tests

(define-module (c99-01)
  #:use-module (nyacc lang c99 parser)
  #:use-module (test-suite lib))

(use-modules (ice-9 pretty-print))

(define incs '("exam.d"))

(define (parse-file file)
  (with-input-from-file file
    (lambda ()
      (parse-c99 #:inc-dirs incs #:mode 'code))))

(define (parse-string str)
  (with-input-from-string str
    (lambda ()
      ;;(simple-format #t "\n~A\n" str)
      (parse-c99 #:inc-dirs incs #:mode 'code))))

(define (parse-string-list . str-l)
  (parse-string (apply string-append str-l)))
   
;; parser test
(with-test-prefix "nyacc/c99-01, CPP"

  ;; add ex04.c

  ;; Get a simple statement to parse.
  (pass-if "include in place"
    (equal?
     (parse-file "exam.d/ex07.c")
     '(trans-unit
       (fctn-defn
	(decl-spec-list (type-spec (fixed-type "int")))
	(ftn-declr (ident "foo") (param-list))
	(compd-stmt
	 (block-item-list
	  (decl (decl-spec-list (type-spec (fixed-type "int")))
		(init-declr-list
		 (init-declr
                  (ident "r")
                  (initzer (p-expr (fixed "1"))))))
	  (expr-stmt
	   (assn-expr
            (p-expr (ident "r"))
            (op "=")
            (add (p-expr (ident "r")) (p-expr (fixed "1")))))
	  (return (p-expr (ident "r")))))))))
  
  (pass-if "skip space and #"
    (equal?
     (parse-file "exam.d/ex08.c")
     '(trans-unit
       (fctn-defn
	(decl-spec-list (type-spec (fixed-type "int")))
	(ftn-declr (ident "foo") (param-list))
	(compd-stmt
	 (block-item-list
	  (expr-stmt
	   (cond-expr
            (p-expr (ident "boo"))
            (cast (type-name (decl-spec-list (type-spec (void))))
                  (p-expr (fixed "0")))
            (fctn-call
	     (p-expr (ident "assert_fail"))
	     (expr-list (p-expr (string "boo"))))))))))))

  (pass-if "#, ##, __VA_ARGS__"
    (equal?
     (parse-file "exam.d/ex12.c")
     '(trans-unit
       (decl (decl-spec-list (type-spec (fixed-type "int")))
	     (init-declr-list
	      (init-declr
	       (ident "x")
	       (initzer
		(add (mul (p-expr (fixed "1")) (p-expr (fixed "2")))
		     (fctn-call
		      (p-expr (ident "bar"))
		      (expr-list (p-expr (fixed "0")) (p-expr (fixed "1"))
				 (p-expr (fixed "2")) (p-expr (fixed "3"))
				 (p-expr (fixed "4"))
				 (mul (p-expr (fixed "2"))
				      (p-expr (fixed "3"))))))))))
       (decl (decl-spec-list
	      (stor-spec (static)) (type-spec (fixed-type "char")))
	     (init-declr-list
	      (init-declr (ptr-declr (pointer) (ident "y"))
			  (initzer (p-expr (string "bar"))))))
       (decl (decl-spec-list (type-spec (fixed-type "int")))
	     (init-declr-list
	      (init-declr
	       (ident "z") (initzer (p-expr (ident "ex12_abc2")))))))))

  (pass-if "spacing in tokenization"
    (let ((sx1 (parse-string "#define foo(X) # X\nchar *s = foo(abc def);\n"))
	  (sx2 (parse-string "#define foo(X) # X\nchar *s = foo(abc  def);\n")))
      (equal? sx1 sx2)))

  (pass-if "def-def-ref"
    (equal?
     (parse-string-list
      "#define A 123\n"
      "#define B A\n"
      "#if B > 0\n"
      "int x;\n"
      "#else\n"
      "char x;\n"
      "#endif\n")
     '(trans-unit
       (decl (decl-spec-list (type-spec (fixed-type "int")))
	     (init-declr-list (init-declr (ident "x")))))))

  (pass-if "repeat in arg"
    (equal?
     (parse-string-list
      "#define bar(x,y) foo(x,y)\n"
      "#define foo(x,y) bar(x,y)\n"
      "#define FOO(x,y) foo(x,y)\n"
      "int x = FOO(FOO(1,2),3);\n")
     '(trans-unit
       (decl (decl-spec-list (type-spec (fixed-type "int")))
	     (init-declr-list
	      (init-declr
	       (ident "x")
	       (initzer
		(fctn-call
		 (p-expr (ident "foo"))
		 (expr-list
                  (fctn-call
		   (p-expr (ident "foo"))
		   (expr-list (p-expr (fixed "1")) (p-expr (fixed "2"))))
                  (p-expr (fixed "3")))))))))))

  (pass-if "multiple"
    (and
     (equal?

      (parse-string "#define inc(X) X++ + 1\nint x = inc(a);")
      '(trans-unit
       (decl (decl-spec-list (type-spec (fixed-type "int")))
	     (init-declr-list
	      (init-declr
	       (ident "x")
	       (initzer
		(add (post-inc (p-expr (ident "a")))
		     (p-expr (fixed "1")))))))))

     (equal?
      (parse-string "#define sqrt(X) sqrt(X)\ndouble x = sqrt(2.0);\n")
      '(trans-unit
	(decl (decl-spec-list
	       (type-spec (float-type "double")))
	      (init-declr-list
	       (init-declr
		(ident "x")
		(initzer
		 (fctn-call
		  (p-expr (ident "sqrt"))
		  (expr-list (p-expr (float "2.0"))))))))))

     #t))

  (pass-if "cond expr w/ undefined idents"
    (equal?
     (parse-string "#if X539101Z\nint x;\n#else\ndouble x;\n#endif\n")
     '(trans-unit
      (decl (decl-spec-list
	     (type-spec (float-type "double")))
	    (init-declr-list (init-declr (ident "x")))))))
    
  (pass-if "include w/ #defined arg "
    (pair? (parse-file "exam.d/ex15.c")))

  ;; This breaks prior to 0.81.0  I think we need more testing on
  ;; possible escapes.  See esc-str in cpp.c.
  (pass-if "CPP string building"
    (if (parse-string-list
	 "int x;\n"
	 "#define XYZ \"bar \\\"-baz\\\"\"\n"
	 "char *s = XYZ;\n") #t #f))

  ;; User needs to #define __has_include __has_include__
  (pass-if "__has_include"
    (and
     (equal?
      (parse-string-list
       "#if __has_include__(\"exam.d/inc.h\")\n"
       "int x;\n"
       "#else\n"
       "char x;\n"
       "#endif\n")
      '(trans-unit
	(decl (decl-spec-list (type-spec (fixed-type "int")))
	      (init-declr-list (init-declr (ident "x"))))))
     (equal?
      (parse-string-list
       "#if __has_include__(\"xxxx.d/inc.h\")\n"
       "int x;\n"
       "#else\n"
       "char x;\n"
       "#endif\n")
      '(trans-unit
	(decl (decl-spec-list (type-spec (fixed-type "char")))
	      (init-declr-list (init-declr (ident "x"))))))
     ))

  (pass-if "Jan's macro-name redef test"
    (equal?
     (parse-file "exam.d/ex17.c")
     '(trans-unit
       (comment " provided by Jan Nieuwenhuizen")
       (comment " worked in 0.80.4, broken in 0.82.1")
       (decl (decl-spec-list
	      (type-spec
	       (enum-def
                (ident "tcc_token")
                (enum-def-list
		 (enum-defn (ident "TOK_LAST"))
		 (enum-defn (ident "TOK_ASM_mov"))))))))))

  (pass-if "Jan's ELF32_ST_INFO macro test"
    (equal?
     (parse-file "exam.d/ex18.c")
     '(trans-unit
       (decl (decl-spec-list (type-spec (fixed-type "int")))
	     (init-declr-list
	      (init-declr
	       (ident "x")
	       (initzer
                (add (lshift
		      (p-expr (fixed "1"))
		      (p-expr (fixed "4")))
                     (bitwise-and
		      (p-expr (fixed "0"))
		      (p-expr (fixed "0xf")))))))))))

  )

;; --- last line ---
