aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/test/lux/tool/compiler/phase/synthesis/function.lux
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--stdlib/source/test/lux/tool/compiler/phase/synthesis/function.lux185
1 files changed, 185 insertions, 0 deletions
diff --git a/stdlib/source/test/lux/tool/compiler/phase/synthesis/function.lux b/stdlib/source/test/lux/tool/compiler/phase/synthesis/function.lux
new file mode 100644
index 000000000..5c6c3f3af
--- /dev/null
+++ b/stdlib/source/test/lux/tool/compiler/phase/synthesis/function.lux
@@ -0,0 +1,185 @@
+(.module:
+ [lux #*
+ [abstract ["." monad (#+ do)]]
+ [data
+ text/format
+ ["." name]]
+ ["r" math/random (#+ Random) ("#@." monad)]
+ ["_" test (#+ Test)]
+ [control
+ pipe]
+ [data
+ ["." product]
+ ["." maybe]
+ ["." error]
+ [number
+ ["." nat]]
+ [collection
+ ["." list ("#@." functor fold)]
+ ["dict" dictionary (#+ Dictionary)]
+ ["." set]]]]
+ ["." // #_
+ ["#." primitive]]
+ {1
+ ["." /
+ ["/#" //
+ ["/#" //
+ [extension
+ ["#." bundle]]
+ ["/#" //
+ ["#." reference (#+ Variable) ("variable@." equivalence)]
+ ["#." analysis (#+ Arity Analysis)]
+ ["#." synthesis (#+ Synthesis)]]]]]})
+
+(def: constant-function
+ (Random [Arity Analysis Analysis])
+ (r.rec
+ (function (_ constant-function)
+ (do r.monad
+ [function? r.bit]
+ (if function?
+ (do @
+ [[arity bodyA predictionA] constant-function]
+ (wrap [(inc arity)
+ (#////analysis.Function (list) bodyA)
+ predictionA]))
+ (do @
+ [predictionA //primitive.primitive]
+ (wrap [0 predictionA predictionA])))))))
+
+(def: (pick scope-size)
+ (-> Nat (Random Nat))
+ (|> r.nat (:: r.monad map (n/% scope-size))))
+
+(def: function-with-environment
+ (Random [Arity Analysis Variable])
+ (do r.monad
+ [num-locals (|> r.nat (:: @ map (|>> (n/% 100) (n/max 10))))
+ #let [indices (list.n/range 0 (dec num-locals))
+ local-env (list@map (|>> #////reference.Local) indices)
+ foreign-env (list@map (|>> #////reference.Foreign) indices)]
+ [arity bodyA predictionA] (: (Random [Arity Analysis Variable])
+ (loop [arity 1
+ current-env foreign-env]
+ (let [current-env/size (list.size current-env)
+ resolver (list@fold (function (_ [idx var] resolver)
+ (dict.put idx var resolver))
+ (: (Dictionary Nat Variable)
+ (dict.new nat.hash))
+ (list.enumerate current-env))]
+ (do @
+ [nest? r.bit]
+ (if nest?
+ (do @
+ [num-picks (:: @ map (n/max 1) (pick (inc current-env/size)))
+ picks (|> (r.set nat.hash num-picks (pick current-env/size))
+ (:: @ map set.to-list))
+ [arity bodyA predictionA] (recur (inc arity)
+ (list@map (function (_ pick)
+ (maybe.assume (list.nth pick current-env)))
+ picks))
+ #let [picked-env (list@map (|>> #////reference.Foreign) picks)]]
+ (wrap [arity
+ (#////analysis.Function picked-env bodyA)
+ predictionA]))
+ (do @
+ [chosen (pick (list.size current-env))]
+ (wrap [arity
+ (#////analysis.Reference (////reference.foreign chosen))
+ (maybe.assume (dict.get chosen resolver))])))))))]
+ (wrap [arity
+ (#////analysis.Function local-env bodyA)
+ predictionA])))
+
+(def: local-function
+ (Random [Arity Analysis Variable])
+ (loop [arity 0
+ nest? #1]
+ (if nest?
+ (do r.monad
+ [nest?' r.bit
+ [arity' bodyA predictionA] (recur (inc arity) nest?')]
+ (wrap [arity'
+ (#////analysis.Function (list) bodyA)
+ predictionA]))
+ (do r.monad
+ [chosen (|> r.nat (:: @ map (|>> (n/% 100) (n/max 2))))]
+ (wrap [arity
+ (#////analysis.Reference (////reference.local chosen))
+ (|> chosen (n/+ (dec arity)) #////reference.Local)])))))
+
+(def: abstraction
+ Test
+ (do r.monad
+ [[arity//constant function//constant prediction//constant] constant-function
+ [arity//environment function//environment prediction//environment] function-with-environment
+ [arity//local function//local prediction//local] local-function]
+ ($_ _.and
+ (_.test "Nested functions will get folded together."
+ (|> function//constant
+ //.phase
+ (///.run [///bundle.empty ////synthesis.init])
+ (case> (^ (#error.Success (////synthesis.function/abstraction [environment arity output])))
+ (and (n/= arity//constant arity)
+ (//primitive.corresponds? prediction//constant output))
+
+ _
+ (n/= 0 arity//constant))))
+ (_.test "Folded functions provide direct access to environment variables."
+ (|> function//environment
+ //.phase
+ (///.run [///bundle.empty ////synthesis.init])
+ (case> (^ (#error.Success (////synthesis.function/abstraction [environment arity (#////synthesis.Reference (////reference.variable output))])))
+ (and (n/= arity//environment arity)
+ (variable@= prediction//environment output))
+
+ _
+ #0)))
+ (_.test "Folded functions properly offset local variables."
+ (|> function//local
+ //.phase
+ (///.run [///bundle.empty ////synthesis.init])
+ (case> (^ (#error.Success (////synthesis.function/abstraction [environment arity (#////synthesis.Reference (////reference.variable output))])))
+ (and (n/= arity//local arity)
+ (variable@= prediction//local output))
+
+ _
+ #0)))
+ )))
+
+(def: application
+ Test
+ (do r.monad
+ [arity (|> r.nat (:: @ map (|>> (n/% 10) (n/max 1))))
+ funcA //primitive.primitive
+ argsA (r.list arity //primitive.primitive)]
+ ($_ _.and
+ (_.test "Can synthesize function application."
+ (|> (////analysis.apply [funcA argsA])
+ //.phase
+ (///.run [///bundle.empty ////synthesis.init])
+ (case> (^ (#error.Success (////synthesis.function/apply [funcS argsS])))
+ (and (//primitive.corresponds? funcA funcS)
+ (list.every? (product.uncurry //primitive.corresponds?)
+ (list.zip2 argsA argsS)))
+
+ _
+ #0)))
+ (_.test "Function application on no arguments just synthesizes to the function itself."
+ (|> (////analysis.apply [funcA (list)])
+ //.phase
+ (///.run [///bundle.empty ////synthesis.init])
+ (case> (#error.Success funcS)
+ (//primitive.corresponds? funcA funcS)
+
+ _
+ #0)))
+ )))
+
+(def: #export test
+ Test
+ (<| (_.context (name.module (name-of /._)))
+ ($_ _.and
+ ..abstraction
+ ..application
+ )))