// testing program.
// it is suppoused to print "Hello world!\n"
// any other output or lack of it signals error
// in Ksi compiler and/or this program.
// in order to see actuall simple Hello world program
// see hello.ksi
!version 1

[
// int sum_up_to(int x)
// {
//   int r;
//   r = 0;
//   for (;;) {
//     r = r + x;
//     if ((x = x - 1) < 0)
//       break;
//   }
// }
  (func .public .int32 sum_up_to
    [(var .int32 x)]
    (bind (compound [
      (var .int32 r)
      (modify (ref r) (const .int32 0))
      (loop (compound [
        (modify (ref r) (plus (ref r) (ref x)))
	(exit_loop (le 
	  (modify (ref x) (minus (ref x) (const .int32 1)))
	  (const .int32 0)))]))
      (return (modify (result) (ref r)))])))
      
	
// int f(int x, int y)
// {
//   return x + 2 * y; 
// }
  (func .public .int32 f 
    [(var .int32 x)
     (var .int32 y)]
      (bind (return (modify 
        (result)
        (plus (ref x) 
	  (mult (const .int32 2) (ref y)))))))

// int excl(int x)
// {
//   if (x == 0)
//     return 1;
//   else
//     return x * excl(x - 1);
// }
  (func .public .int32 excl
    [(var .int32 x)]
    (bind (cond (eq (ref x) (const .int32 0))
      (return (modify (result) (const .int32 1)))
      (return (modify (result) 
        (mult (ref x) (call excl 
	  [(minus (ref x) (const .int32 1))])))))))

// double exclf(double x)
// {
//   if (x == 0.0)
//     return 1.0;
//   else
//     return x * exclf(x - 1.0);
// }
  (func .public .c_double exclf
    [(var .c_double x)]
    (bind (cond (eq (ref x) (const .c_double 0.0))
      (return (modify (result) (const .c_double 1.0)))
      (return (modify (result) 
        (mult (ref x) (call exclf 
	  [(minus (ref x) (const .c_double 1.0))])))))))

// int fib(int x)
// {
//   if (x < 2)
//     return 1;
//   else
//     // this call is marked as having no side effects
//     return fib(x-1) + fib(x-2);
// }
  (func .local .int32 fib 
    [(var .int32 x)]
    (bind (cond (lt (ref x) (const .int32 2))
      (return (modify (result) (const .int32 1)))
      (return (modify (result) (plus 
        (fcall fib [(minus (ref x) (const .int32 1))]) 
	(fcall fib [(minus (ref x) (const .int32 2))])))))))
  

  (func .local .int32 do_test_block [(var .bool x)]
    (bind (compound [
      (block b
        (cond (ref x)
	  (exit b)
	  (return (modify (result) (const .int32 0)))))
      (return (modify (result) (const .int32 1)))])))
    
  (func .local .int32 test_block []
    (bind 
      (return (modify (result)
        (plus
	  (mult
            (plus 
	      (call do_test_block [.true]) 
	      (const .int32 8))
	    (const .int32 2))
          (call do_test_block [.false]))))))

  (func .extern .void abort [] (nil))
  (func .extern .int32 printf [(var .ptr str) (ellipsis)] (nil))
  
  (func .public .int8 test_int8 
    [(var .int8 v1) 
     (var .int8 v2) 
     (var .int8 v3)]
    (bind (return (modify (result) (mult (ref v1) (plus (ref v2) (ref v3)))))))

  // returns abs(2x+10)
  (func .public .int32 test_labels [(var .int32 x)]
    (bind (compound [
      // first .local flow
      (goto somewhere)
      (label hell)
      (modify (ref x) (plus (ref x) (const .int32 10)))
      (goto next_test)
      (label somewhere)
      (modify (ref x) (plus (ref x) (ref x)))
      (goto hell)
      (label next_test)
      // here x = 2*x + 10
      (cond (gt (ref x) (const .int32 0))
        (goto bye)
	(goto sign))
      (call abort [])
      (label sign)
      (modify (ref x) (negate (ref x))) 
      (goto bye)
      (label bye)
      (return (modify (result) (ref x)))])))
 
  (func .local .int64 test64 [(var .int8 x) (var .int64 y)]
    (bind
      (return (modify (result) 
        (mult (convert .int64 (ref x)) (ref y))))))

  (func .local .void assert [(var .bool test)]
     (bind (cond (ref test)
       (nop)
       (call abort []))))
       
  (func .local .bool test_scopes [(var .c_double x)]
    (bind (compound [
      (var .c_double y)
      (modify (ref y) (ref x))
      (bind (var .int32 x))
      (bind (compound [
        (var .c_double x)
	(modify (ref x) (plus (ref y) (const .c_double 1.0)))]))
      (call assert [(eq (ref x) (ref y))])
      (return (modify (result)
        (truth_and 
	  (gt (ref x) (const .c_double 0.99))
	  (lt (ref x) (const .c_double 1.01)))))])))
 
  (func .local .void modd [(var (ptr .c_double) p)]
    (bind (modify 
      (indirect (ref p)) 
      (plus 
        (indirect (ref p)) 
	(const .c_double 3.0)))))
      
  (func .local .void mod16 [(var (ptr .int16) p)]
    (bind (modify 
      (indirect (ref p)) 
      (plus 
        (indirect (ref p)) 
	(const .int16 3)))))
      
  (func .local .void mod32 [(var (ptr .int32) p)]
    (bind (modify 
      (indirect (ref p)) 
      (plus 
        (indirect (ref p)) 
	(const .int32 3)))))
      
  (func .local .void test_addr []
    (bind (compound [
      (var .int32 x)
      (var .int16 y)
      (var .c_double z)
      (modify (ref x) (const .int32 10))
      (modify (ref y) (const .int16 10))
      (modify (ref z) (const .c_double 10.01))
      (call mod32 [(addr (ref x))])
      (call mod16 [(addr (ref y))])
      (call modd [(addr (ref z))])
      (call assert [(eq (ref x) (const .int32 13))])
      (call assert [(eq (ref x) (convert .int32 (ref y)))])
      (call assert [(eq (ref x) (convert .int32 (ref z)))])])))

  (structdef st [
    (var (int 32) i)
    (var (int 32) i2)
    (var .c_double d)
    (var .c_double d2)])

  (structdef node [
    (var (ptr (struct node)) next)
    (var (int 32) data)])
  
  (func .local (void) fix_struct [(var (ptr (struct st)) p)]
    (bind (compound [
      (modify 
        (component (indirect (ref p)) i2) 
	(mult (component (indirect (ref p)) i) (const (int 32) 2)))
      (modify 
        (component (indirect (ref p)) d2) 
	(mult (component (indirect (ref p)) d) (const .c_double 2.0)))])))

  (func .local (void) check_double [(var (ptr .c_double) p)] (nil))
  (func .local (void) check_int32 [(var (ptr (int 32)) p)] (nil))
  
  (func .local (void) test_struct []
    (bind (compound [
      (var (struct st) s)
      (modify (component (ref s) i) (const (int 32) 10))
      (modify (component (ref s) d) (const .c_double 10.0))
      (call fix_struct [(addr (ref s))])
      (call check_int32 [(addr (component (ref s) i))])
      // i guess this isn't 100% portable...
      // GCC can layout this diffrently.
      (call check_double [(addr (component (ref s) d))])])))

  (func .local (void) check_int32 [(var (ptr (int 32)) p)]
    (bind (compound [
      (cond (eq
        (mult (indirect (ref p)) (const (int 32) 2)) 
	(indirect (plus (ref p) (const .ptr_off 4))))
	  (nop)
	  (call abort []))])))
	  
  (func .local (void) check_double [(var (ptr .c_double) p)]
    (bind (compound [
      (cond (eq
        (mult (indirect (ref p)) (const .c_double 2.0)) 
	(indirect (plus (ref p) (const .ptr_off .c_double_bytes))))
	  (nop)
	  (call abort []))])))
	  
  (global .local (var (int 32) local_x))
  
  (func .local (int 32) test_static_sub []
    (bind (compound [
      (static_var (int 32) static_x)
      (modify (ref static_x) (plus (ref static_x) (const (int 32) 3)))
      (modify (ref local_x) (plus (ref local_x) (const (int 32) 4)))
      (return (modify (result) (plus (ref static_x) (ref local_x))))])))
      
  (func .local (void) test_static []
    (bind 
      (cond (eq 
        (plus 
          (call test_static_sub [])
          (call test_static_sub []))
	(const (int 32) 21))
	(nop)
	(call abort []))))

  (func .local .void test_convert [(var .c_double x)]
    (bind
      (cond (lt
        (minus (ref x) (float_expr .c_double (fix_trunc .int32 (ref x))))
	(const .c_double 0.5))
	  (call assert [(eq 
	    (plus (convert .int32 (ref x)) (fix_trunc .int32 (ref x)))
	    (fix_trunc .int32
	      (plus (ref x) (ref x))))])
	  (call assert [(eq 
	    (plus (fix_trunc .int32 (ref x)) (fix_trunc .int32 (ref x)))
	    (fix_trunc .int32
	      (minus (plus (ref x) (ref x)) (const .c_double 1.0))))]))))

  (structdef jmp_buf
    [
      (var (ptr (void)) w1)
      (var (ptr (void)) w2)
      (var (ptr (void)) w3)
      (var (ptr (void)) w4)
      (var (ptr (void)) w5)])
    
  (func .local (void) jumpaway [(var (ptr (struct jmp_buf)) jmpbuf)]
    (bind (compound [
      (builtin longjmp [(ref jmpbuf) (const .c_int 1)])
      (call abort [])])))
      
  (func .local (void) test_setjmp []
    (bind (compound [
      (var (struct jmp_buf) buf)
      (cond 
        (eq (builtin setjmp [(addr (ref buf))]) (const .c_int 0))
	(nop)
	(return (nop)))
      (call jumpaway [(addr (ref buf))])
      (call abort [])])))
  
  (global .public (var (int 32) cnt))
  
  (func .local (void) f1 [(var (int 32) x)]
    (bind (modify (ref cnt) (plus (ref cnt) (ref x)))))
      
  (func .local (void) f2 [(var (int 32) x)]
    (bind (modify (ref cnt) (plus (ref cnt) (plus (ref x) (ref x))))))
  
  (func .local (void) call123 
    [(var (func_ptr (void) [(int 32)]) fnc)]
    (bind (compound [
      (ptr_call (ref fnc) [(const (int 32) 1)])
      (ptr_call (ref fnc) [(const (int 32) 2)])
      (ptr_call (ref fnc) [(const (int 32) 3)])])))

  (func .local (void) test_func_ptr []
    (bind (compound [
      (call call123 [(func_ref f1)])
      (cond (eq (ref cnt) (const (int 32) 6))
        (nop)
	(call abort []))
      (call call123 [(func_ref f2)])
      (cond (eq (ref cnt) (const (int 32) 18))
        (nop)
	(call abort []))])))

  (func .local (void) test_prepost []
    (bind (compound [
      (var (int 32) x)
      (var (int 32) y)
      (var (int 32) z)
      (modify (ref x) (const (int 32) 1))
      (modify (ref y) (const (int 32) 2))
      (modify (ref z) (plus 
        (preinc (ref x) (const (int 32) 1))
        (postinc (ref y) (const (int 32) 1))))
      (call assert [(eq (ref z) (const (int 32) 4))])
      (modify (ref z) (minus (ref z) (plus
        (postdec (ref x) (const (int 32) 1))
	(predec (ref y) (const (int 32) 1)))))
      (call assert [(eq (ref z) (const (int 32) 0))])
      (modify (ref z) (minus 
        (preinc (ref x) (const (int 32) 13))
        (postinc (ref y) (const (int 32) 12))))
      (call assert [(eq (ref z) (const (int 32) 12))])
      (call assert [(eq (ref x) (ref y))])])))
  
  // test against regression
  (func .public .int32 foo [(var .int32 x)] (nil))
  (func .public .int32 foo [(var .int32 y)] 
    (bind (return (modify (result) (ref y)))))
  (func .public (ptr (uint 8)) bar [] 
    (bind (return (modify (result) (.string "foo". "bar". "baz")))))

  (func .public (void) test_try_finally []
    (bind (compound [
      (var .int32 x)
      (var .int32 y)
      (modify (ref x) (const .int32 1))
      (modify (ref y)
        (try_finally 
          (modify (ref x) (plus (ref x) (const .int32 2)))
          (modify (ref x) (plus (ref x) (const .int32 3)))))
      (call assert [(truth_andif (eq (ref x) (const .int32 6))
      				      (eq (ref y) (const .int32 3)))])
      (block foo
        (try_finally 
	(compound [
            (modify (ref x) (plus (ref x) (const .int32 4)))
	    (exit foo)
            (modify (ref x) (plus (ref x) (const .int32 5)))])
          (modify (ref x) (plus (ref x) (const .int32 6)))))
      (call assert [(truth_andif (eq (ref x) (const .int32 16))
      				      (eq (ref y) (const .int32 3)))])])))

  (abstract_type [(export_name "<<XXX>123>sdas")] blah_type)
  (tyalias (var (poly) ty))
  
  (func .public (tyref ty) id [(var (tyref ty) x)]
    (bind (return (modify (result) (ref x)))))

  (enumdef [(entry ENUM_foo 1) (entry ENUM_bar 2)])
  
  (func [(public) (export_name "bl...ahoobar")] (void) poly_test []
    (bind (compound [
      (call 
        assert 
        [(eq 
          (convert .c_long (call id [(const .c_long 123)])) 
	  (const .c_long 123))])
      (var (ptr (poly)) foo)
      (call 
        assert 
        [(eq 
          (convert .c_long (call id [(enumref ENUM_bar)])) 
	  (const .c_long 2))])
      (call
        assert
	[(eq 
	  (convert (ptr (ptr (poly))) (call id [(addr (ref foo))]))
	  (addr (ref foo)))])])))

  (func .public .int32 main []
    (bind (compound [
      (var .int32 x)
      (modify (ref x) (const .int32 3))
      (modify (ref x) (call f [(ref x) (call f [(ref x) (ref x)])]))
      (cond (eq (ref x) (const .int32 21))
	(compound [(call printf [(.string "H")]) (nop)])
	(nop))
      (modify (ref x) (plus (ref x) (fcall fib [(const .int32 10)])))
      (cond (truth_and 
        (lt (ref x) (const .int32 111))
        (gt (ref x) (const .int32 109)))
	(compound [(call printf [(.string "e")]) (nop)])
	(nop))
      (truth_andif
        (eq (call test_block []) (const .int32 18))
        (compound [
	  (call printf [(.string "l")])
	  .true]))
      (truth_orif
        (ne (ref x) (const .int32 110))
        (compound 
	  [(call printf [(.string "l")])
	  .true]))
      (cond (eq (call test_int8 
        [(const .int8 3) (const .int8 2) (const .int8 5)])
	  (const .int8 21))
             (call printf [(.string "o")])
             (call printf [(.string "OOPS!\n")]))
	     
      (cond (truth_and
        (eq (call test_labels [(const .int32 12)]) (const .int32 34))
        (eq (call test_labels [(const .int32 -12)]) (const .int32 14)))
             (call printf [(.string " ")])
             (call printf [(.string "OOPS!\n")]))

      (cond (eq (call test64 
        [(const .int8 3) (const .int64 1111111111111)])
	(const .int64 3333333333333))
         (call printf [(.string "w")])
         (call printf [(.string "OOPS!\n")]))
	
      (cond (call test_scopes [(const .c_double 1.0)])
         (call printf [(.string "o")])
         (call printf [(.string "OOPS!\n")]))

      (call test_try_finally [])
      (call test_convert [(const .c_double 3.1415)])
      (call test_convert [(const .c_double 2.7)])
      (call test_convert [(const .c_double 122.7)])
      (call printf [(.string "r")])
      
      (call test_addr [])
      (call printf [(.string "l")])
      
      (call test_struct [])
      (call test_static [])
      (call printf [(.string "d")])
      
      (call test_setjmp [])
      (call test_func_ptr [])
      (call test_prepost [])
      (call poly_test [])

      (call printf [(.string "!\n")])
      (return (modify (result) (const .int32 0)))])))
]
