mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			300 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Janet
		
	
	
	
	
	
			
		
		
	
	
			300 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Janet
		
	
	
	
	
	
| # Copyright (c) 2023 Calvin Rose
 | |
| #
 | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| # of this software and associated documentation files (the "Software"), to
 | |
| # deal in the Software without restriction, including without limitation the
 | |
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 | |
| # sell copies of the Software, and to permit persons to whom the Software is
 | |
| # furnished to do so, subject to the following conditions:
 | |
| #
 | |
| # The above copyright notice and this permission notice shall be included in
 | |
| # all copies or substantial portions of the Software.
 | |
| #
 | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | |
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | |
| # IN THE SOFTWARE.
 | |
| 
 | |
| (import ./helper :prefix "" :exit true)
 | |
| (start-suite)
 | |
| 
 | |
| # Set global variables to prevent some possible compiler optimizations
 | |
| # that defeat point of the test
 | |
| # 2771171
 | |
| (var zero 0)
 | |
| (var one 1)
 | |
| (var two 2)
 | |
| (var three 3)
 | |
| (var plus +)
 | |
| (assert (= 22 (plus one (plus 1 2 two) (plus 8 (plus zero 1) 4 three)))
 | |
|         "nested function calls")
 | |
| 
 | |
| # McCarthy's 91 function
 | |
| # 2771171
 | |
| (var f91 nil)
 | |
| (set f91 (fn [n]
 | |
|            (if (> n 100)
 | |
|              (- n 10)
 | |
|              (f91 (f91 (+ n 11))))))
 | |
| (assert (= 91 (f91 10)) "f91(10) = 91")
 | |
| (assert (= 91 (f91 11)) "f91(11) = 91")
 | |
| (assert (= 91 (f91 20)) "f91(20) = 91")
 | |
| (assert (= 91 (f91 31)) "f91(31) = 91")
 | |
| (assert (= 91 (f91 100)) "f91(100) = 91")
 | |
| (assert (= 91 (f91 101)) "f91(101) = 91")
 | |
| (assert (= 92 (f91 102)) "f91(102) = 92")
 | |
| (assert (= 93 (f91 103)) "f91(103) = 93")
 | |
| (assert (= 94 (f91 104)) "f91(104) = 94")
 | |
| 
 | |
| # Fibonacci
 | |
| # 23196ff
 | |
| (def fib
 | |
|   (do
 | |
|     (var fib nil)
 | |
|     (set fib (fn [n]
 | |
|                (if (< n 2)
 | |
|                  n
 | |
|                  (+ (fib (- n 1)) (fib (- n 2))))))))
 | |
| (def fib2
 | |
|   (fn fib2 [n]
 | |
|     (if (< n 2)
 | |
|       n
 | |
|       (+ (fib2 (- n 1)) (fib2 (- n 2))))))
 | |
| 
 | |
| (assert (= (fib 0) (fib2 0) 0) "fib(0)")
 | |
| (assert (= (fib 1) (fib2 1) 1) "fib(1)")
 | |
| (assert (= (fib 2) (fib2 2) 1) "fib(2)")
 | |
| (assert (= (fib 3) (fib2 3) 2) "fib(3)")
 | |
| (assert (= (fib 4) (fib2 4) 3) "fib(4)")
 | |
| (assert (= (fib 5) (fib2 5) 5) "fib(5)")
 | |
| (assert (= (fib 6) (fib2 6) 8) "fib(6)")
 | |
| (assert (= (fib 7) (fib2 7) 13) "fib(7)")
 | |
| (assert (= (fib 8) (fib2 8) 21) "fib(8)")
 | |
| (assert (= (fib 9) (fib2 9) 34) "fib(9)")
 | |
| (assert (= (fib 10) (fib2 10) 55) "fib(10)")
 | |
| 
 | |
| # Closure in non function scope
 | |
| # 911b0b1
 | |
| (def outerfun (fn [x y]
 | |
|                 (def c (do
 | |
|                          (def someval (+ 10 y))
 | |
|                          (def ctemp (if x (fn [] someval) (fn [] y)))
 | |
|                          ctemp
 | |
|                          ))
 | |
|                 (+ 1 2 3 4 5 6 7)
 | |
|                 c))
 | |
| 
 | |
| (assert (= ((outerfun 1 2)) 12) "inner closure 1")
 | |
| (assert (= ((outerfun nil 2)) 2) "inner closure 2")
 | |
| (assert (= ((outerfun false 3)) 3) "inner closure 3")
 | |
| 
 | |
| # d6967a5
 | |
| ((fn []
 | |
|    (var accum 1)
 | |
|    (var counter 0)
 | |
|    (while (< counter 16)
 | |
|      (set accum (blshift accum 1))
 | |
|      (set counter (+ 1 counter)))
 | |
|    (assert (= accum 65536) "loop in closure")))
 | |
| 
 | |
| (var accum 1)
 | |
| (var counter 0)
 | |
| (while (< counter 16)
 | |
|   (set accum (blshift accum 1))
 | |
|   (set counter (+ 1 counter)))
 | |
| (assert (= accum 65536) "loop globally")
 | |
| 
 | |
| # Fiber tests
 | |
| # 21bd960
 | |
| (def afiber (fiber/new (fn []
 | |
|                          (def x (yield))
 | |
|                          (error (string "hello, " x))) :ye))
 | |
| 
 | |
| (resume afiber) # first resume to prime
 | |
| (def afiber-result (resume afiber "world!"))
 | |
| 
 | |
| (assert (= afiber-result "hello, world!") "fiber error result")
 | |
| (assert (= (fiber/status afiber) :error) "fiber error status")
 | |
| 
 | |
| # Var arg tests
 | |
| # f054586
 | |
| (def vargf (fn [more] (apply + more)))
 | |
| 
 | |
| (assert (= 0 (vargf @[])) "var arg no arguments")
 | |
| (assert (= 1 (vargf @[1])) "var arg no packed arguments")
 | |
| (assert (= 3 (vargf @[1 2])) "var arg tuple size 1")
 | |
| (assert (= 10 (vargf @[1 2 3 4])) "var arg tuple size 2, 2 normal args")
 | |
| (assert (= 110 (vargf @[1 2 3 4 10 10 10 10 10 10 10 10 10 10]))
 | |
|         "var arg large tuple")
 | |
| 
 | |
| # Higher order functions
 | |
| # d9f24ef
 | |
| (def compose (fn [f g] (fn [& xs] (f (apply g xs)))))
 | |
| 
 | |
| (def -+ (compose - +))
 | |
| (def +- (compose + -))
 | |
| 
 | |
| (assert (= (-+ 1 2 3 4) -10) "compose - +")
 | |
| (assert (= (+- 1 2 3 4) -8) "compose + -")
 | |
| (assert (= ((compose -+ +-) 1 2 3 4) 8) "compose -+ +-")
 | |
| (assert (= ((compose +- -+) 1 2 3 4) 10) "compose +- -+")
 | |
| 
 | |
| # UTF-8
 | |
| # d9f24ef
 | |
| #🐙🐙🐙🐙
 | |
| 
 | |
| (defn foo [Θa Θb Θc] 0)
 | |
| (def 🦊 :fox)
 | |
| (def 🐮 :cow)
 | |
| (assert (= (string "🐼" 🦊 🐮) "🐼foxcow") "emojis 🙉 :)")
 | |
| (assert (not= 🦊 "🦊") "utf8 strings are not symbols and vice versa")
 | |
| (assert (= "\U01F637" "😷") "unicode escape 1")
 | |
| (assert (= "\u2623" "\U002623" "☣") "unicode escape 2")
 | |
| (assert (= "\u24c2" "\U0024c2" "Ⓜ") "unicode escape 3")
 | |
| (assert (= "\u0061" "a") "unicode escape 4")
 | |
| 
 | |
| # Test max triangle program
 | |
| # c0e373f
 | |
| # Find the maximum path from the top (root)
 | |
| # of the triangle to the leaves of the triangle.
 | |
| 
 | |
| (defn myfold [xs ys]
 | |
|   (let [xs1 [;xs 0]
 | |
|         xs2 [0 ;xs]
 | |
|         m1 (map + xs1 ys)
 | |
|         m2 (map + xs2 ys)]
 | |
|     (map max m1 m2)))
 | |
| 
 | |
| (defn maxpath [t]
 | |
|  (extreme > (reduce myfold () t)))
 | |
| 
 | |
| # Test it
 | |
| # Maximum path is 3 -> 10 -> 3 -> 9 for a total of 25
 | |
| (def triangle '[
 | |
|  [3]
 | |
|  [7 10]
 | |
|  [4 3 7]
 | |
|  [8 9 1 3]
 | |
| ])
 | |
| 
 | |
| (assert (= (maxpath triangle) 25) `max triangle`)
 | |
| 
 | |
| # Large functions
 | |
| # 6822400
 | |
| (def manydefs (seq [i :range [0 300]]
 | |
|                 (tuple 'def (gensym) (string "value_" i))))
 | |
| (array/push manydefs (tuple * 10000 3 5 7 9))
 | |
| (def f (compile ['do ;manydefs] (fiber/getenv (fiber/current))))
 | |
| (assert (= (f) (* 10000 3 5 7 9)) "long function compilation")
 | |
| 
 | |
| # Closure in while loop
 | |
| # abe7d59
 | |
| (def closures (seq [i :range [0 5]] (fn [] i)))
 | |
| (assert (= 0 ((get closures 0))) "closure in loop 0")
 | |
| (assert (= 1 ((get closures 1))) "closure in loop 1")
 | |
| (assert (= 2 ((get closures 2))) "closure in loop 2")
 | |
| (assert (= 3 ((get closures 3))) "closure in loop 3")
 | |
| (assert (= 4 ((get closures 4))) "closure in loop 4")
 | |
| 
 | |
| # Another regression test - no segfaults
 | |
| # 6b4824c
 | |
| (defn afn [x] x)
 | |
| (var afn-var afn)
 | |
| (var identity-var identity)
 | |
| (var map-var map)
 | |
| (var not-var not)
 | |
| (assert (= 1 (try (afn-var) ([err] 1))) "bad arity 1")
 | |
| (assert (= 4 (try ((fn [x y] (+ x y)) 1) ([_] 4))) "bad arity 2")
 | |
| (assert (= 1 (try (identity-var) ([err] 1))) "bad arity 3")
 | |
| (assert (= 1 (try (map-var) ([err] 1))) "bad arity 4")
 | |
| (assert (= 1 (try (not-var) ([err] 1))) "bad arity 5")
 | |
| 
 | |
| # Detaching closure over non resumable fiber
 | |
| # issue #317 - 7c4ffe9b9
 | |
| (do
 | |
|   (defn f1
 | |
|     [a]
 | |
|     (defn f1 [] (++ (a 0)))
 | |
|     (defn f2 [] (++ (a 0)))
 | |
|     (error [f1 f2]))
 | |
|   (def [_ [f1 f2]] (protect (f1 @[0])))
 | |
|   # At time of writing, mark phase can detach closure envs.
 | |
|   (gccollect)
 | |
|   (assert (= 1 (f1)) "detach-non-resumable-closure 1")
 | |
|   (assert (= 2 (f2)) "detach-non-resumable-closure 2"))
 | |
| 
 | |
| # Dynamic defs
 | |
| # ec65f03
 | |
| (def staticdef1 0)
 | |
| (defn staticdef1-inc [] (+ 1 staticdef1))
 | |
| (assert (= 1 (staticdef1-inc)) "before redefinition without :redef")
 | |
| (def staticdef1 1)
 | |
| (assert (= 1 (staticdef1-inc)) "after redefinition without :redef")
 | |
| (setdyn :redef true)
 | |
| (def dynamicdef2 0)
 | |
| (defn dynamicdef2-inc [] (+ 1 dynamicdef2))
 | |
| (assert (= 1 (dynamicdef2-inc)) "before redefinition with dyn :redef")
 | |
| (def dynamicdef2 1)
 | |
| (assert (= 2 (dynamicdef2-inc)) "after redefinition with dyn :redef")
 | |
| (setdyn :redef nil)
 | |
| 
 | |
| # missing symbols
 | |
| # issue #914 - 1eb34989d
 | |
| (defn lookup-symbol [sym] (defglobal sym 10) (dyn sym))
 | |
| 
 | |
| (setdyn :missing-symbol lookup-symbol)
 | |
| 
 | |
| (assert (= (eval-string "(+ a 5)") 15) "lookup missing symbol")
 | |
| 
 | |
| (setdyn :missing-symbol nil)
 | |
| (setdyn 'a nil)
 | |
| 
 | |
| (assert-error "compile error" (eval-string "(+ a 5)"))
 | |
| 
 | |
| # 88813c4
 | |
| (assert (deep= (in (disasm (defn a [] (def x 10) x)) :symbolmap)
 | |
|                @[[0 2 0 'a] [0 2 1 'x]])
 | |
|         "symbolmap when *debug* is true")
 | |
| 
 | |
| (defn a [arg]
 | |
|   (def x 10)
 | |
|   (do
 | |
|     (def y 20)
 | |
|     (def z 30)
 | |
|     (+ x y z)))
 | |
| (def symbolslots (in (disasm a) :symbolslots))
 | |
| (def f (asm (disasm a)))
 | |
| (assert (deep= (in (disasm f) :symbolslots)
 | |
|                symbolslots)
 | |
|         "symbolslots survive disasm/asm")
 | |
| 
 | |
| (comment
 | |
|   (setdyn *debug* true)
 | |
|   (setdyn :pretty-format "%.40M")
 | |
|   (def f (fn [x] (fn [y] (+ x y))))
 | |
|   (assert (deep= (map last (in (disasm (f 10)) :symbolmap))
 | |
|                  @['x 'y])
 | |
|           "symbolmap upvalues"))
 | |
| 
 | |
| (assert (deep= (in (disasm (defn a [arg]
 | |
|                              (def x 10)
 | |
|                              (do
 | |
|                                (def y 20)
 | |
|                                (def z 30)
 | |
|                                (+ x y z)))) :symbolmap)
 | |
|                @[[0 6 0 'arg]
 | |
|                  [0 6 1 'a]
 | |
|                  [0 6 2 'x]
 | |
|                  [1 6 3 'y]
 | |
|                  [2 6 4 'z]])
 | |
|         "arg & inner symbolmap")
 | |
| 
 | |
| # 4782a76
 | |
| (assert (= 10 (do (var x 10) (def y x) (++ x) y)) "no invalid aliasing")
 | |
| 
 | |
| (end-suite)
 | |
| 
 | 
