From 00ca2ba61759b59a17b59c56b347f83f089fabd5 Mon Sep 17 00:00:00 2001 From: Eduardo Julian Date: Wed, 3 Jun 2020 01:25:47 -0400 Subject: Properly implemented and enabled loop optimization. --- stdlib/source/lux/control/io.lux | 16 +- stdlib/source/lux/control/parser/binary.lux | 5 + .../lux/tool/compiler/language/lux/analysis.lux | 2 +- .../language/lux/phase/synthesis/function.lux | 33 +- .../compiler/language/lux/phase/synthesis/loop.lux | 376 +++++++-------------- .../lux/tool/compiler/language/lux/synthesis.lux | 202 ++++++++--- .../lux/tool/compiler/reference/variable.lux | 5 +- stdlib/source/test/lux/control.lux | 2 + stdlib/source/test/lux/control/parser/binary.lux | 359 ++++++++++++++++++++ .../tool/compiler/language/lux/phase/synthesis.lux | 4 +- .../compiler/language/lux/phase/synthesis/loop.lux | 293 ++++++++++++++++ 11 files changed, 971 insertions(+), 326 deletions(-) create mode 100644 stdlib/source/test/lux/control/parser/binary.lux create mode 100644 stdlib/source/test/lux/tool/compiler/language/lux/phase/synthesis/loop.lux (limited to 'stdlib') diff --git a/stdlib/source/lux/control/io.lux b/stdlib/source/lux/control/io.lux index 533e321b9..ed28b338e 100644 --- a/stdlib/source/lux/control/io.lux +++ b/stdlib/source/lux/control/io.lux @@ -17,9 +17,9 @@ {#.doc "A type that represents synchronous, effectful computations that may interact with the outside world."} (-> Any a) - (def: (label thunk) + (def: label (All [a] (-> (-> Any a) (IO a))) - (:abstraction thunk)) + (|>> :abstraction)) (template: (!io computation) (:abstraction (template.with-locals [g!func g!arg] @@ -49,17 +49,23 @@ (All [a] (-> (IO a) a)) (|>> !run)) - (structure: #export functor (Functor IO) + (structure: #export functor + (Functor IO) + (def: (map f) (|>> !run f !io))) - (structure: #export apply (Apply IO) + (structure: #export apply + (Apply IO) + (def: &functor ..functor) (def: (apply ff fa) (!io ((!run ff) (!run fa))))) - (structure: #export monad (Monad IO) + (structure: #export monad + (Monad IO) + (def: &functor ..functor) (def: wrap (|>> !io)) diff --git a/stdlib/source/lux/control/parser/binary.lux b/stdlib/source/lux/control/parser/binary.lux index 3dc061940..ed698ccd1 100644 --- a/stdlib/source/lux/control/parser/binary.lux +++ b/stdlib/source/lux/control/parser/binary.lux @@ -50,6 +50,11 @@ (function (_ (^@ input [offset data])) (#try.Success [input (n.= offset (/.size data))]))) +(def: #export offset + (Parser Offset) + (function (_ (^@ input [offset data])) + (#try.Success [input offset]))) + (def: #export remaining (Parser Nat) (function (_ (^@ input [offset data])) diff --git a/stdlib/source/lux/tool/compiler/language/lux/analysis.lux b/stdlib/source/lux/tool/compiler/language/lux/analysis.lux index c9bc95612..71009473a 100644 --- a/stdlib/source/lux/tool/compiler/language/lux/analysis.lux +++ b/stdlib/source/lux/tool/compiler/language/lux/analysis.lux @@ -103,7 +103,7 @@ _ false))) -(structure: (composite-equivalence (^open "/@.")) +(structure: #export (composite-equivalence (^open "/@.")) (All [a] (-> (Equivalence a) (Equivalence (Composite a)))) (def: (= reference sample) diff --git a/stdlib/source/lux/tool/compiler/language/lux/phase/synthesis/function.lux b/stdlib/source/lux/tool/compiler/language/lux/phase/synthesis/function.lux index e34c78f71..890722aeb 100644 --- a/stdlib/source/lux/tool/compiler/language/lux/phase/synthesis/function.lux +++ b/stdlib/source/lux/tool/compiler/language/lux/phase/synthesis/function.lux @@ -3,7 +3,8 @@ [abstract ["." monad (#+ do)]] [control - ["ex" exception (#+ exception:)]] + [pipe (#+ case>)] + ["." exception (#+ exception:)]] [data ["." maybe] ["." text @@ -22,10 +23,11 @@ ["." phase ("#@." monad)]]]]) (exception: #export (cannot-find-foreign-variable-in-environment {foreign Register} {environment Environment}) - (ex.report ["Foreign" (%.nat foreign)] - ["Environment" (|> environment - (list@map ////reference/variable.format) - (text.join-with " "))])) + (exception.report + ["Foreign" (%.nat foreign)] + ["Environment" (|> environment + (list@map ////reference/variable.format) + (text.join-with " "))])) (def: arity-arguments (-> Arity (List Synthesis)) @@ -46,15 +48,15 @@ (let [[funcA argsA] (////analysis.application exprA)] (do {@ phase.monad} [funcS (phase archive funcA) - argsS (monad.map @ (phase archive) argsA) - ## locals /.locals - ] + argsS (monad.map @ (phase archive) argsA)] (with-expansions [ (as-is (/.function/apply [funcS argsS]))] (case funcS - ## (^ (/.function/abstraction functionS)) - ## (wrap (|> functionS - ## (//loop.loop (get@ #/.environment functionS) locals argsS) - ## (maybe.default ))) + (^ (/.function/abstraction functionS)) + (do @ + [locals /.locals] + (wrap (|> functionS + (//loop.optimization locals argsS) + (maybe.default )))) (^ (/.function/apply [funcS' argsS'])) (wrap (/.function/apply [funcS' (list@compose argsS' argsS)])) @@ -69,7 +71,7 @@ (phase@wrap aliased) #.None - (phase.throw cannot-find-foreign-variable-in-environment [register environment]))) + (phase.throw ..cannot-find-foreign-variable-in-environment [register environment]))) (def: (grow-path grow path) (-> (-> Synthesis (Operation Synthesis)) Path (Operation Path)) @@ -172,7 +174,7 @@ (do {@ phase.monad} [initsS+' (monad.map @ (grow environment) initsS+) iterationS' (grow environment iterationS)] - (wrap (/.loop/scope [start initsS+' iterationS']))) + (wrap (/.loop/scope [(inc start) initsS+' iterationS']))) (#/.Recur argumentsS+) (|> argumentsS+ @@ -209,7 +211,8 @@ (def: #export (abstraction phase environment archive bodyA) (-> Phase Environment Phase) (do {@ phase.monad} - [bodyS (phase archive bodyA)] + [bodyS (/.with-locals 2 + (phase archive bodyA))] (case bodyS (^ (/.function/abstraction [env' down-arity' bodyS'])) (|> bodyS' diff --git a/stdlib/source/lux/tool/compiler/language/lux/phase/synthesis/loop.lux b/stdlib/source/lux/tool/compiler/language/lux/phase/synthesis/loop.lux index 5aa644e18..b4a43ce23 100644 --- a/stdlib/source/lux/tool/compiler/language/lux/phase/synthesis/loop.lux +++ b/stdlib/source/lux/tool/compiler/language/lux/phase/synthesis/loop.lux @@ -1,303 +1,171 @@ (.module: - [lux (#- loop) + [lux #* [abstract ["." monad (#+ do)]] - [control - ["p" parser]] [data ["." maybe ("#@." monad)] [number ["n" nat]] [collection - ["." list ("#@." functor)]]] - [macro - ["." code] - ["." syntax]]] - ["." /// #_ - ## TODO: Remove the 'extension' import ASAP. - ["#." extension] - [// - ["#." analysis (#+ Environment)] - ["/" synthesis (#+ Path Abstraction Synthesis)] - [/// - ["#." reference - ["#/." variable (#+ Register Variable)]]]]]) + ["." list]]]] + [//// + ["." analysis (#+ Environment)] + ["/" synthesis (#+ Path Abstraction Synthesis)] + [/// + [arity (#+ Arity)] + ["." reference + ["." variable (#+ Register Variable)]]]]) (type: #export (Transform a) (-> a (Maybe a))) -(def: (some? maybe) - (All [a] (-> (Maybe a) Bit)) - (case maybe - (#.Some _) #1 - #.None #0)) +(def: #export (register-optimization offset) + (-> Register (-> Register Register)) + (|>> dec (n.+ offset))) -(template: #export (self) - (#/.Reference (///reference.local 0))) +(def: (variable-optimization offset environment variable) + (-> Register Environment (Transform Variable)) + (case variable + (^ (variable.self)) + #.None + + (#variable.Foreign register) + (list.nth register environment) -(template: (recursive-apply args) - (#/.Apply (self) args)) + (#variable.Local register) + (#.Some (#variable.Local (register-optimization offset register))))) -(def: improper #0) -(def: proper #1) - -(def: (proper? exprS) - (-> Synthesis Bit) - (case exprS - (^ (self)) - ..improper - - (#/.Structure structure) - (case structure - (#///analysis.Variant variantS) - (proper? (get@ #///analysis.value variantS)) - - (#///analysis.Tuple membersS+) - (list.every? proper? membersS+)) - - (#/.Control controlS) - (case controlS - (#/.Branch branchS) - (case branchS - (#/.Case inputS pathS) - (and (proper? inputS) - (.loop [pathS pathS] - (case pathS - (^or (#/.Alt leftS rightS) (#/.Seq leftS rightS)) - (and (recur leftS) (recur rightS)) - - (#/.Then bodyS) - (proper? bodyS) - - _ - ..proper))) - - (#/.Let inputS register bodyS) - (and (proper? inputS) - (proper? bodyS)) - - (#/.If inputS thenS elseS) - (and (proper? inputS) - (proper? thenS) - (proper? elseS)) - - (#/.Get members inputS) - (proper? inputS)) - - (#/.Loop loopS) - (case loopS - (#/.Scope scopeS) - (and (list.every? proper? (get@ #/.inits scopeS)) - (proper? (get@ #/.iteration scopeS))) - - (#/.Recur argsS) - (list.every? proper? argsS)) - - (#/.Function functionS) - (case functionS - (#/.Abstraction environment arity bodyS) - (list.every? ///reference/variable.self? environment) - - (#/.Apply funcS argsS) - (and (proper? funcS) - (list.every? proper? argsS)))) - - (#/.Extension [name argsS]) - (list.every? proper? argsS) - - _ - ..proper)) - -(def: (path-recursion synthesis-recursion) - (-> (Transform Synthesis) (Transform Path)) - (function (recur pathS) - (case pathS - (#/.Alt leftS rightS) - (let [leftS' (recur leftS) - rightS' (recur rightS)] - (if (or (some? leftS') - (some? rightS')) - (#.Some (#/.Alt (maybe.default leftS leftS') - (maybe.default rightS rightS'))) - #.None)) - - (#/.Seq leftS rightS) - (maybe@map (|>> (#/.Seq leftS)) (recur rightS)) - - (#/.Then bodyS) - (maybe@map (|>> #/.Then) (synthesis-recursion bodyS)) - - _ - #.None))) - -(def: #export (recursion arity) - (-> Nat (Transform Synthesis)) - (function (recur exprS) - (case exprS - (#/.Control controlS) - (case controlS - (#/.Branch branchS) - (case branchS - (#/.Case inputS pathS) - (|> pathS - (path-recursion recur) - (maybe@map (|>> (#/.Case inputS) #/.Branch #/.Control))) - - (#/.Let inputS register bodyS) - (maybe@map (|>> (#/.Let inputS register) #/.Branch #/.Control) - (recur bodyS)) - - (#/.If inputS thenS elseS) - (let [thenS' (recur thenS) - elseS' (recur elseS)] - (if (or (some? thenS') - (some? elseS')) - (#.Some (|> (#/.If inputS - (maybe.default thenS thenS') - (maybe.default elseS elseS')) - #/.Branch #/.Control)) - #.None)) - - (#/.Get members inputS) - #.None) - - (^ (#/.Function (recursive-apply argsS))) - (if (n.= arity (list.size argsS)) - (#.Some (|> argsS #/.Recur #/.Loop #/.Control)) - #.None) - - _ - #.None) - - _ - #.None))) - -(def: (resolve environment) - (-> Environment (Transform Variable)) - (function (_ variable) - (case variable - (#///reference/variable.Foreign register) - (list.nth register environment) - - _ - (#.Some variable)))) - -(def: (adjust-path adjust-synthesis offset) +(def: (path-optimization body-optimization offset) (-> (Transform Synthesis) Register (Transform Path)) - (function (recur pathS) - (case pathS + (function (recur path) + (case path (#/.Bind register) - (#.Some (#/.Bind (n.+ offset register))) + (#.Some (#/.Bind (register-optimization offset register))) (^template [] - ( leftS rightS) + ( left right) (do maybe.monad - [leftS' (recur leftS) - rightS' (recur rightS)] - (wrap ( leftS' rightS')))) + [left' (recur left) + right' (recur right)] + (wrap ( left' right')))) ([#/.Alt] [#/.Seq]) - (#/.Then bodyS) - (|> bodyS adjust-synthesis (maybe@map (|>> #/.Then))) + (#/.Then body) + (|> body + body-optimization + (maybe@map (|>> #/.Then))) _ - (#.Some pathS)))) - -(def: (adjust scope-environment offset) - (-> Environment Register (Transform Synthesis)) - (function (recur exprS) - (case exprS - (#/.Structure structureS) - (case structureS - (#///analysis.Variant variantS) + (#.Some path)))) + +(def: (body-optimization offset scope-environment arity expr) + (-> Register Environment Arity (Transform Synthesis)) + (loop [return? true + expr expr] + (case expr + (#/.Primitive _) + (#.Some expr) + + (#/.Structure structure) + (case structure + (#analysis.Variant variant) (do maybe.monad - [valueS' (|> variantS (get@ #///analysis.value) recur)] - (wrap (|> variantS - (set@ #///analysis.value valueS') - #///analysis.Variant - #/.Structure))) + [value' (|> variant (get@ #analysis.value) (recur false))] + (wrap (|> variant + (set@ #analysis.value value') + /.variant))) - (#///analysis.Tuple membersS+) - (|> membersS+ - (monad.map maybe.monad recur) - (maybe@map (|>> #///analysis.Tuple #/.Structure)))) + (#analysis.Tuple tuple) + (|> tuple + (monad.map maybe.monad (recur false)) + (maybe@map (|>> /.tuple)))) (#/.Reference reference) (case reference - (^ (///reference.constant constant)) - (#.Some exprS) + (^ (#reference.Variable (variable.self))) + #.None + + (^ (reference.constant constant)) + (#.Some expr) - (^ (///reference.local register)) - (#.Some (#/.Reference (///reference.local (n.+ offset register)))) + (^ (reference.local register)) + (#.Some (#/.Reference (reference.local (register-optimization offset register)))) - (^ (///reference.foreign register)) + (^ (reference.foreign register)) (|> scope-environment (list.nth register) - (maybe@map (|>> #///reference.Variable #/.Reference)))) + (maybe@map (|>> #reference.Variable #/.Reference)))) - (^ (/.branch/case [inputS pathS])) + (^ (/.branch/case [input path])) (do maybe.monad - [inputS' (recur inputS) - pathS' (adjust-path recur offset pathS)] - (wrap (|> pathS' [inputS'] /.branch/case))) + [input' (recur false input) + path' (path-optimization (recur return?) offset path)] + (wrap (|> path' [input'] /.branch/case))) - (^ (/.branch/let [inputS register bodyS])) + (^ (/.branch/let [input register body])) (do maybe.monad - [inputS' (recur inputS) - bodyS' (recur bodyS)] - (wrap (/.branch/let [inputS' register bodyS']))) + [input' (recur false input) + body' (recur return? body)] + (wrap (/.branch/let [input' (register-optimization offset register) body']))) - (^ (/.branch/if [inputS thenS elseS])) + (^ (/.branch/if [input then else])) (do maybe.monad - [inputS' (recur inputS) - thenS' (recur thenS) - elseS' (recur elseS)] - (wrap (/.branch/if [inputS' thenS' elseS']))) + [input' (recur false input) + then' (recur return? then) + else' (recur return? else)] + (wrap (/.branch/if [input' then' else']))) - (^ (/.loop/scope scopeS)) + (^ (/.branch/get [path record])) (do maybe.monad - [inits' (|> scopeS + [record (recur false record)] + (wrap (/.branch/get [path record]))) + + (^ (/.loop/scope scope)) + (do {@ maybe.monad} + [inits' (|> scope (get@ #/.inits) - (monad.map maybe.monad recur)) - iteration' (recur (get@ #/.iteration scopeS))] - (wrap (/.loop/scope {#/.start (|> scopeS (get@ #/.start) (n.+ offset)) + (monad.map @ (recur false))) + iteration' (recur return? (get@ #/.iteration scope))] + (wrap (/.loop/scope {#/.start (|> scope (get@ #/.start) (register-optimization offset)) #/.inits inits' #/.iteration iteration'}))) - (^ (/.loop/recur argsS)) - (|> argsS - (monad.map maybe.monad recur) + (^ (/.loop/recur args)) + (|> args + (monad.map maybe.monad (recur false)) (maybe@map (|>> /.loop/recur))) - - (^ (/.function/abstraction [environment arity bodyS])) - (do maybe.monad - [environment' (monad.map maybe.monad - (resolve scope-environment) + (^ (/.function/abstraction [environment arity body])) + (do {@ maybe.monad} + [environment' (monad.map @ (variable-optimization offset scope-environment) environment)] - (wrap (/.function/abstraction [environment' arity bodyS]))) + (wrap (/.function/abstraction [environment' arity body]))) - (^ (/.function/apply [function arguments])) - (do maybe.monad - [function' (recur function) - arguments' (monad.map maybe.monad recur arguments)] - (wrap (/.function/apply [function' arguments']))) - - (#/.Extension [name argsS]) - (|> argsS - (monad.map maybe.monad recur) - (maybe@map (|>> [name] #/.Extension))) - - _ - (#.Some exprS)))) - -(def: #export (loop environment num-locals inits functionS) - (-> Environment Nat (List Synthesis) Abstraction (Maybe Synthesis)) - (let [bodyS (get@ #/.body functionS)] - (if (and (n.= (list.size inits) - (get@ #/.arity functionS)) - (proper? bodyS)) - (|> bodyS - (adjust environment num-locals) - (maybe@map (|>> [(inc num-locals) inits] /.loop/scope))) - #.None))) + (^ (/.function/apply [abstraction arguments])) + (do {! maybe.monad} + [arguments' (monad.map maybe.monad (recur false) arguments)] + (case abstraction + (^ (#/.Reference (#reference.Variable (variable.self)))) + (if (and return? + (n.= arity (list.size arguments))) + (wrap (/.loop/recur arguments')) + #.None) + + _ + (do ! + [abstraction' (recur false abstraction)] + (wrap (/.function/apply [abstraction' arguments']))))) + + (#/.Extension [name args]) + (|> args + (monad.map maybe.monad (recur false)) + (maybe@map (|>> [name] #/.Extension)))))) + +(def: #export (optimization offset inits functionS) + (-> Register (List Synthesis) Abstraction (Maybe Synthesis)) + (if (n.= (get@ #/.arity functionS) + (list.size inits)) + (|> (get@ #/.body functionS) + (body-optimization offset (get@ #/.environment functionS) (get@ #/.arity functionS)) + (maybe@map (|>> [offset inits] /.loop/scope))) + #.None)) diff --git a/stdlib/source/lux/tool/compiler/language/lux/synthesis.lux b/stdlib/source/lux/tool/compiler/language/lux/synthesis.lux index a88d986fc..06f84d90d 100644 --- a/stdlib/source/lux/tool/compiler/language/lux/synthesis.lux +++ b/stdlib/source/lux/tool/compiler/language/lux/synthesis.lux @@ -4,8 +4,9 @@ [monad (#+ do)] [equivalence (#+ Equivalence)]] [control - ["ex" exception (#+ exception:)]] + ["." exception (#+ exception:)]] [data + ["." sum] ["." bit ("#@." equivalence)] ["." text ("#@." equivalence) ["%" format (#+ Format format)]] @@ -16,15 +17,15 @@ [collection ["." list ("#@." functor)] ["." dictionary (#+ Dictionary)]]]] - ["." // #_ - ["#." analysis (#+ Environment Composite Analysis)] + [// + ["." analysis (#+ Environment Composite Analysis)] [phase ["." extension (#+ Extension)]] [/// [arity (#+ Arity)] - ["#." reference (#+ Reference) - ["#/." variable (#+ Register Variable)]] - ["#." phase]]]) + ["." phase] + ["." reference (#+ Reference) + ["." variable (#+ Register Variable)]]]]) (type: #export Resolver (Dictionary Variable Variable)) @@ -33,7 +34,7 @@ (def: #export fresh-resolver Resolver - (dictionary.new //reference/variable.hash)) + (dictionary.new variable.hash)) (def: #export init State @@ -202,7 +203,7 @@ (def: #export with-new-local (All [a] (-> (Operation a) (Operation a))) - (<<| (do //phase.monad + (<<| (do phase.monad [locals ..locals]) (..with-locals (inc locals)))) @@ -222,8 +223,8 @@ content))] - [variant #//analysis.Variant] - [tuple #//analysis.Tuple] + [variant #analysis.Variant] + [tuple #analysis.Tuple] ) (template [ ] @@ -232,10 +233,10 @@ content))] - [variable //reference.variable] - [constant //reference.constant] - [variable/local //reference.local] - [variable/foreign //reference.foreign] + [variable reference.variable] + [constant reference.constant] + [variable/local reference.local] + [variable/foreign reference.foreign] ) (template [ ] @@ -327,36 +328,35 @@ (#Structure structure) (case structure - (#//analysis.Variant [lefts right? content]) + (#analysis.Variant [lefts right? content]) (|> (%synthesis content) (format (%.nat lefts) " " (%.bit right?) " ") (text.enclose ["(" ")"])) - (#//analysis.Tuple members) + (#analysis.Tuple members) (|> members (list@map %synthesis) (text.join-with " ") (text.enclose ["[" "]"]))) (#Reference reference) - (//reference.format reference) + (reference.format reference) (#Control control) (case control (#Function function) (case function (#Abstraction [environment arity body]) - (|> (%synthesis body) - (format (%.nat arity) " ") - (format (|> environment - (list@map //reference/variable.format) - (text.join-with " ") - (text.enclose ["[" "]"])) - " ") - (text.enclose ["(" ")"])) + (let [environment' (|> environment + (list@map variable.format) + (text.join-with " ") + (text.enclose ["[" "]"]))] + (|> (format environment' " " (%.nat arity) " " (%synthesis body)) + (text.enclose ["(#function " ")"]))) (#Apply func args) - (|> (list@map %synthesis args) + (|> args + (list@map %synthesis) (text.join-with " ") (format (%synthesis func) " ") (text.enclose ["(" ")"]))) @@ -364,7 +364,7 @@ (#Branch branch) (case branch (#Let input register body) - (|> (format (%synthesis input) " " (%.nat register) " " (%synthesis body)) + (|> (format (%.nat register) " " (%synthesis input) " " (%synthesis body)) (text.enclose ["(#let " ")"])) (#If test then else) @@ -381,9 +381,22 @@ (|> (format (%synthesis input) " " (%path' %synthesis path)) (text.enclose ["(#case " ")"]))) - ## (#Loop loop) - _ - "???") + (#Loop loop) + (case loop + (#Scope scope) + (|> (format (%.nat (get@ #start scope)) + " " (|> (get@ #inits scope) + (list@map %synthesis) + (text.join-with " ") + (text.enclose ["[" "]"])) + " " (%synthesis (get@ #iteration scope))) + (text.enclose ["(#loop " ")"])) + + (#Recur args) + (|> args + (list@map %synthesis) + (text.join-with " ") + (text.enclose ["(#recur " ")"])))) (#Extension [name args]) (|> (list@map %synthesis args) @@ -395,7 +408,9 @@ (Format Path) (%path' %synthesis)) -(structure: #export primitive-equivalence (Equivalence Primitive) +(structure: #export primitive-equivalence + (Equivalence Primitive) + (def: (= reference sample) (case [reference sample] (^template [ ] @@ -411,27 +426,29 @@ _ false))) -(structure: #export access-equivalence (Equivalence Access) +(def: side-equivalence + (Equivalence Side) + (sum.equivalence n.equivalence n.equivalence)) + +(def: member-equivalence + (Equivalence Member) + (sum.equivalence n.equivalence n.equivalence)) + +(structure: #export access-equivalence + (Equivalence Access) + (def: (= reference sample) (case [reference sample] - (^template [] - [( reference') ( sample')] - (case [reference' sample'] - (^template [] - [( reference'') ( sample'')] - (n.= reference'' sample'')) - ([#.Left] - [#.Right]) - - _ - false)) - ([#Side] - [#Member]) + (^template [ ] + [( reference) ( sample)] + (:: = reference sample)) + ([#Side ..side-equivalence] + [#Member ..member-equivalence]) _ false))) -(structure: #export (path'-equivalence Equivalence) +(structure: #export (path'-equivalence equivalence) (All [a] (-> (Equivalence a) (Equivalence (Path' a)))) (def: (= reference sample) @@ -444,7 +461,7 @@ (:: = reference' sample')) ([#Test primitive-equivalence] [#Access access-equivalence] - [#Then Equivalence]) + [#Then equivalence]) [(#Bind reference') (#Bind sample')] (n.= reference' sample') @@ -459,13 +476,100 @@ _ false))) -(structure: #export equivalence (Equivalence Synthesis) +(structure: (branch-equivalence (^open "/@.")) + (All [a] (-> (Equivalence a) (Equivalence (Branch a)))) + + (def: (= reference sample) + (case [reference sample] + [(#Let [reference-input reference-register reference-body]) + (#Let [sample-input sample-register sample-body])] + (and (/@= reference-input sample-input) + (n.= reference-register sample-register) + (/@= reference-body sample-body)) + + [(#If [reference-test reference-then reference-else]) + (#If [sample-test sample-then sample-else])] + (and (/@= reference-test sample-test) + (/@= reference-then sample-then) + (/@= reference-else sample-else)) + + [(#Get [reference-path reference-record]) + (#Get [sample-path sample-record])] + (and (:: (list.equivalence ..member-equivalence) = reference-path sample-path) + (/@= reference-record sample-record)) + + [(#Case [reference-input reference-path]) + (#Case [sample-input sample-path])] + (and (/@= reference-input sample-input) + (:: (path'-equivalence /@=) = reference-path sample-path)) + + _ + false))) + +(structure: (loop-equivalence (^open "/@.")) + (All [a] (-> (Equivalence a) (Equivalence (Loop a)))) + + (def: (= reference sample) + (case [reference sample] + [(#Scope [reference-start reference-inits reference-iteration]) + (#Scope [sample-start sample-inits sample-iteration])] + (and (n.= reference-start sample-start) + (:: (list.equivalence /@=) = reference-inits sample-inits) + (/@= reference-iteration sample-iteration)) + + [(#Recur reference) (#Recur sample)] + (:: (list.equivalence /@=) = reference sample) + + _ + false))) + +(structure: (function-equivalence (^open "/@.")) + (All [a] (-> (Equivalence a) (Equivalence (Function a)))) + + (def: (= reference sample) + (case [reference sample] + [(#Abstraction [reference-environment reference-arity reference-body]) + (#Abstraction [sample-environment sample-arity sample-body])] + (and (:: (list.equivalence variable.equivalence) = reference-environment sample-environment) + (n.= reference-arity sample-arity) + (/@= reference-body sample-body)) + + [(#Apply [reference-abstraction reference-arguments]) + (#Apply [sample-abstraction sample-arguments])] + (and (/@= reference-abstraction sample-abstraction) + (:: (list.equivalence /@=) = reference-arguments sample-arguments)) + + _ + false))) + +(structure: (control-equivalence (^open "/@.")) + (All [a] (-> (Equivalence a) (Equivalence (Control a)))) + + (def: (= reference sample) + (case [reference sample] + (^template [ ] + [( reference) ( sample)] + (:: ( /@=) = reference sample)) + ([#Branch ..branch-equivalence] + [#Loop ..loop-equivalence] + [#Function ..function-equivalence]) + + _ + false))) + +(structure: #export equivalence + (Equivalence Synthesis) + (def: (= reference sample) (case [reference sample] (^template [ ] [( reference') ( sample')] (:: = reference' sample')) - ([#Primitive primitive-equivalence]) + ([#Primitive ..primitive-equivalence] + [#Structure (analysis.composite-equivalence =)] + [#Reference reference.equivalence] + [#Control (control-equivalence =)] + [#Extension (extension.equivalence =)]) _ false))) diff --git a/stdlib/source/lux/tool/compiler/reference/variable.lux b/stdlib/source/lux/tool/compiler/reference/variable.lux index 10c080c6e..e0c814e8d 100644 --- a/stdlib/source/lux/tool/compiler/reference/variable.lux +++ b/stdlib/source/lux/tool/compiler/reference/variable.lux @@ -42,9 +42,12 @@ (#Foreign register) (|> register .int (i.* -1) .nat))))) +(template: #export (self) + (#..Local 0)) + (def: #export self? (-> Variable Bit) - (|>> (case> (^ (#Local 0)) + (|>> (case> (^ (..self)) true _ diff --git a/stdlib/source/test/lux/control.lux b/stdlib/source/test/lux/control.lux index 7fc1c428d..29c34b430 100644 --- a/stdlib/source/test/lux/control.lux +++ b/stdlib/source/test/lux/control.lux @@ -21,6 +21,7 @@ ["#." io] ["#." parser ["#/." analysis] + ["#/." binary] ["#/." text] ["#/." cli]] ["#." pipe] @@ -59,6 +60,7 @@ ($_ _.and /parser.test /parser/analysis.test + /parser/binary.test /parser/text.test /parser/cli.test )) diff --git a/stdlib/source/test/lux/control/parser/binary.lux b/stdlib/source/test/lux/control/parser/binary.lux new file mode 100644 index 000000000..d646852f3 --- /dev/null +++ b/stdlib/source/test/lux/control/parser/binary.lux @@ -0,0 +1,359 @@ +(.module: + [lux (#- primitive) + ["_" test (#+ Test)] + [abstract + [equivalence (#+ Equivalence)] + [monad (#+ do)]] + [control + ["." try] + ["." exception] + ["<>" parser]] + [data + ["." binary] + ["." sum] + ["." maybe] + ["." bit] + ["." name] + ["." text ("#@." equivalence) + ["." encoding]] + ["." format #_ + ["#" binary]] + [number + ["." i64] + ["n" nat] + ["." int] + ["." rev] + ["." frac]] + [collection + ["." list] + ["." row] + ["." set]]] + [macro + ["." code]] + ["." type] + [math + ["." random (#+ Random)]]] + {1 + ["." /]}) + +(template: (!expect ) + (case + + true + + _ + false)) + +(def: segment-size 10) + +(def: random-name + (Random Name) + (random.and (random.unicode ..segment-size) + (random.unicode ..segment-size))) + +(structure: cursor-equivalence + (Equivalence Cursor) + + (def: (= [expected-module expected-line expected-column] + [sample-module sample-line sample-column]) + (and (text@= expected-module sample-module) + (n.= expected-line sample-line) + (n.= expected-column sample-column)))) + +(def: random-cursor + (Random Cursor) + ($_ random.and + (random.unicode ..segment-size) + random.nat + random.nat)) + +(def: random-code + (Random Code) + (random.rec + (function (_ recur) + (let [random-sequence (do {@ random.monad} + [size (:: @ map (n.% 2) random.nat)] + (random.list size recur))] + ($_ random.and + ..random-cursor + (: (Random (Code' (Ann Cursor))) + ($_ random.or + random.bit + random.nat + random.int + random.rev + random.frac + (random.unicode ..segment-size) + ..random-name + ..random-name + random-sequence + random-sequence + (do {@ random.monad} + [size (:: @ map (n.% 2) random.nat)] + (random.list size (random.and recur recur))) + ))))))) + +(def: random-type + (Random Type) + (let [(^open ".") random.monad] + ($_ random.either + (wrap .Nat) + (wrap .List) + (wrap .Code) + (wrap .Type)))) + +(def: size + Test + (<| (_.with-cover [/.Size]) + (`` ($_ _.and + (~~ (template [ ] + [(do {@ random.monad} + [expected (:: @ map (i64.and (i64.mask )) + random.nat)] + (_.cover [ ] + (|> (format.run expected) + (/.run ) + (!expect (^multi (#try.Success actual) + (n.= (.nat expected) + (.nat actual)))))))] + + [/.size/8 /.bits/8 format.bits/8] + [/.size/16 /.bits/16 format.bits/16] + [/.size/32 /.bits/32 format.bits/32] + [/.size/64 /.bits/64 format.bits/64] + )))))) + +(def: binary + Test + (`` ($_ _.and + (~~ (template [ ] + [(do {@ random.monad} + [expected (:: @ map encoding.to-utf8 (random.ascii ..segment-size))] + (_.cover [] + (|> (format.run expected) + (/.run ) + (!expect (^multi (#try.Success actual) + (:: binary.equivalence = expected actual))))))] + + [/.binary/8 format.binary/8] + [/.binary/16 format.binary/16] + [/.binary/32 format.binary/32] + [/.binary/64 format.binary/64] + ))))) + +(def: utf8 + Test + (`` ($_ _.and + (~~ (template [ ] + [(do {@ random.monad} + [expected (random.ascii ..segment-size)] + (_.cover [] + (|> (format.run expected) + (/.run ) + (!expect (^multi (#try.Success actual) + (:: text.equivalence = expected actual))))))] + + [/.utf8/8 format.utf8/8] + [/.utf8/16 format.utf8/16] + [/.utf8/32 format.utf8/32] + [/.utf8/64 format.utf8/64] + [/.text format.utf8/64] + ))))) + +(def: row + Test + (`` ($_ _.and + (~~ (template [ ] + [(do {@ random.monad} + [expected (random.row ..segment-size random.nat)] + (_.cover [] + (|> expected + (format.run ( format.nat)) + (/.run ( /.nat)) + (!expect (^multi (#try.Success actual) + (:: (row.equivalence n.equivalence) = expected actual))))))] + + [/.row/8 format.row/8] + [/.row/16 format.row/16] + [/.row/32 format.row/32] + [/.row/64 format.row/64] + ))))) + +(def: simple + Test + (`` ($_ _.and + (~~ (template [ ] + [(do {@ random.monad} + [expected ] + (_.cover [] + (|> (format.run expected) + (/.run ) + (!expect (^multi (#try.Success actual) + (:: = expected actual))))))] + + [/.bit format.bit random.bit bit.equivalence] + [/.nat format.nat random.nat n.equivalence] + [/.int format.int random.int int.equivalence] + [/.rev format.rev random.rev rev.equivalence] + [/.frac format.frac random.frac frac.equivalence] + )) + (do {@ random.monad} + [expected (:: @ map (|>> (i64.and (i64.mask /.size/8)) + (n.max 2)) + random.nat)] + (_.cover [/.not-a-bit] + (|> expected + (format.run format.bits/8) + (/.run /.bit) + (!expect (^multi (#try.Failure error) + (exception.match? /.not-a-bit error)))))) + ))) + +(def: complex + Test + (`` ($_ _.and + (~~ (template [ ] + [(do {@ random.monad} + [expected ] + (_.cover [] + (|> (format.run expected) + (/.run ) + (!expect (^multi (#try.Success actual) + (:: = expected actual))))))] + + [/.cursor format.cursor random-cursor cursor-equivalence] + [/.code format.code random-code code.equivalence] + [/.type format.type random-type type.equivalence] + )) + (~~ (template [ ] + [(do {@ random.monad} + [expected ] + (_.cover [] + (|> (format.run expected) + (/.run ) + (!expect (^multi (#try.Success actual) + (:: = expected actual))))))] + + [/.maybe (/.maybe /.nat) (format.maybe format.nat) (random.maybe random.nat) (maybe.equivalence n.equivalence)] + [/.list (/.list /.nat) (format.list format.nat) (random.list ..segment-size random.nat) (list.equivalence n.equivalence)] + [/.set (/.set n.hash /.nat) (format.set format.nat) (random.set n.hash ..segment-size random.nat) set.equivalence] + [/.name /.name format.name ..random-name name.equivalence] + )) + (do {@ random.monad} + [expected (:: @ map (list.repeat ..segment-size) random.nat)] + (_.cover [/.set-elements-are-not-unique] + (|> expected + (format.run (format.list format.nat)) + (/.run (/.set n.hash /.nat)) + (!expect (^multi (#try.Failure error) + (exception.match? /.set-elements-are-not-unique error)))))) + (do {@ random.monad} + [expected (random.or random.bit random.nat)] + (_.cover [/.or] + (|> expected + (format.run (format.or format.bit format.nat)) + (/.run (: (/.Parser (Either Bit Nat)) + (/.or /.bit /.nat))) + (!expect (^multi (#try.Success actual) + (:: (sum.equivalence bit.equivalence n.equivalence) = + expected + actual)))))) + (do {@ random.monad} + [tag (:: @ map (|>> (i64.and (i64.mask /.size/8)) + (n.max 2)) + random.nat) + value random.bit] + (_.cover [/.invalid-tag] + (|> [tag value] + (format.run (format.and format.bits/8 format.bit)) + (/.run (: (/.Parser (Either Bit Nat)) + (/.or /.bit /.nat))) + (!expect (^multi (#try.Failure error) + (exception.match? /.invalid-tag error)))))) + (do {@ random.monad} + [expected (random.list ..segment-size random.nat)] + (_.cover [/.rec] + (|> expected + (format.run (format.list format.nat)) + (/.run (: (/.Parser (List Nat)) + (/.rec + (function (_ recur) + (/.or /.any + (<>.and /.nat + recur)))))) + (!expect (^multi (#try.Success actual) + (:: (list.equivalence n.equivalence) = + expected + actual)))))) + ))) + +(def: #export test + Test + (<| (_.covering /._) + (_.with-cover [/.Parser]) + (`` ($_ _.and + (_.cover [/.run /.any] + (|> (binary.create 0) + (/.run /.any) + (!expect (#try.Success _)))) + (do {@ random.monad} + [data (:: @ map encoding.to-utf8 (random.ascii ..segment-size))] + (_.cover [/.binary-was-not-fully-read] + (|> data + (/.run /.any) + (!expect (^multi (#try.Failure error) + (exception.match? /.binary-was-not-fully-read error)))))) + (do {@ random.monad} + [expected (:: @ map encoding.to-utf8 (random.ascii ..segment-size))] + (_.cover [/.segment] + (|> expected + (/.run (/.segment ..segment-size)) + (!expect (^multi (#try.Success actual) + (:: binary.equivalence = expected actual)))))) + (do {@ random.monad} + [data (:: @ map encoding.to-utf8 (random.ascii ..segment-size))] + (_.cover [/.end?] + (|> data + (/.run (do <>.monad + [pre /.end? + _ (/.segment ..segment-size) + post /.end?] + (wrap (and (not pre) + post)))) + (!expect (#try.Success #1))))) + (do {@ random.monad} + [to-read (:: @ map (n.% (inc ..segment-size)) random.nat) + data (:: @ map encoding.to-utf8 (random.ascii ..segment-size))] + (_.cover [/.Offset /.offset] + (|> data + (/.run (do <>.monad + [start /.offset + _ (/.segment to-read) + offset /.offset + _ (/.segment (n.- to-read ..segment-size)) + nothing-left /.offset] + (wrap (and (n.= 0 start) + (n.= to-read offset) + (n.= ..segment-size nothing-left))))) + (!expect (#try.Success #1))))) + (do {@ random.monad} + [to-read (:: @ map (n.% (inc ..segment-size)) random.nat) + data (:: @ map encoding.to-utf8 (random.ascii ..segment-size))] + (_.cover [/.remaining] + (|> data + (/.run (do <>.monad + [_ (/.segment to-read) + remaining /.remaining + _ (/.segment (n.- to-read ..segment-size)) + nothing-left /.remaining] + (wrap (and (n.= ..segment-size + (n.+ to-read remaining)) + (n.= 0 nothing-left))))) + (!expect (#try.Success #1))))) + ..size + ..binary + ..utf8 + ..row + ..simple + ..complex + )))) diff --git a/stdlib/source/test/lux/tool/compiler/language/lux/phase/synthesis.lux b/stdlib/source/test/lux/tool/compiler/language/lux/phase/synthesis.lux index da9937862..46291b311 100644 --- a/stdlib/source/test/lux/tool/compiler/language/lux/phase/synthesis.lux +++ b/stdlib/source/test/lux/tool/compiler/language/lux/phase/synthesis.lux @@ -5,7 +5,8 @@ ["#." primitive] ["#." structure] ["#." case] - ["#." function]]) + ["#." function] + ["#." loop]]) (def: #export test Test @@ -14,4 +15,5 @@ /structure.test /case.test /function.test + /loop.test )) diff --git a/stdlib/source/test/lux/tool/compiler/language/lux/phase/synthesis/loop.lux b/stdlib/source/test/lux/tool/compiler/language/lux/phase/synthesis/loop.lux new file mode 100644 index 000000000..adb98ba3a --- /dev/null +++ b/stdlib/source/test/lux/tool/compiler/language/lux/phase/synthesis/loop.lux @@ -0,0 +1,293 @@ +(.module: + [lux (#- primitive structure loop function) + ["_" test (#+ Test)] + [abstract + [monad (#+ do)]] + [control + ["." try] + ["." exception]] + [data + [number + ["n" nat]] + [collection + ["." list ("#@." functor)]]] + [math + ["." random (#+ Random) ("#@." monad)]]] + {1 + ["." / + [//// + ["." analysis (#+ Environment)] + ["/#" synthesis (#+ Member Path Synthesis)] + [/// + [arity (#+ Arity)] + ["." reference (#+ Constant) + ["." variable (#+ Register Variable)]]]]]}) + +(type: (Scenario a) + (-> Register Arity Register (Random [Register [a a]]))) + +(def: (primitive offset arity next) + (Scenario Synthesis) + (`` ($_ random.either + (~~ (template [ ] + [(do {@ random.monad} + [example (:: @ map (|>> ) )] + (wrap [next + [example + example]]))] + + [//.bit random.bit] + [//.i64 (:: @ map .i64 random.nat)] + [//.f64 random.frac] + [//.text (random.unicode 1)] + )) + ))) + +(def: (constant offset arity next) + (Scenario Constant) + (do random.monad + [name (random.and (random.unicode 1) + (random.unicode 1))] + (wrap [next + [name + name]]))) + +(def: (variable offset arity next) + (Scenario Variable) + (let [local (do {@ random.monad} + [register (:: @ map (|>> (n.% arity) inc) random.nat)] + (wrap [next + [(#variable.Local (/.register-optimization offset register)) + (#variable.Local register)]]))] + (case offset + 0 local + _ ($_ random.either + local + (do {@ random.monad} + [foreign (:: @ map (n.% offset) random.nat)] + (wrap [next + [(#variable.Local foreign) + (#variable.Foreign foreign)]])))))) + +(def: (reference offset arity next) + (Scenario Synthesis) + (`` ($_ random.either + (~~ (template [ ] + [(do {@ random.monad} + [[next [exampleE exampleA]] ( offset arity next)] + (wrap [next + [( exampleE) + ( exampleA)]]))] + + [//.constant ..constant] + [//.variable ..variable] + ))))) + +(def: (structure offset arity next) + (Scenario Synthesis) + ($_ random.either + (do {@ random.monad} + [lefts random.nat + right? random.bit + [next [valueE valueA]] (..reference offset arity next)] + (wrap [next + [(//.variant + {#analysis.lefts lefts + #analysis.right? right? + #analysis.value valueE}) + (//.variant + {#analysis.lefts lefts + #analysis.right? right? + #analysis.value valueA})]])) + (do {@ random.monad} + [[next [leftE leftA]] (..reference offset arity next) + [next [rightE rightA]] (..reference offset arity next)] + (wrap [next + [(//.tuple (list leftE rightE)) + (//.tuple (list leftA rightA))]])) + )) + +(def: path + (Scenario Path) + (let [pattern (: (Scenario Path) + (.function (recur offset arity next) + (`` ($_ random.either + (random@wrap [next + [//.path/pop + //.path/pop]]) + (~~ (template [ ] + [(do {@ random.monad} + [example (:: @ map (|>> ) )] + (wrap [next + [example + example]]))] + + [//.path/bit random.bit] + [//.path/i64 (:: @ map .i64 random.nat)] + [//.path/f64 random.frac] + [//.path/text (random.unicode 1)] + )) + (~~ (template [] + [(do {@ random.monad} + [example (:: @ map (|>> ) + (random.or random.nat + random.nat))] + (wrap [next + [example + example]]))] + + [//.path/side] + [//.path/member] + )) + (random@wrap [(inc next) + [(//.path/bind (/.register-optimization offset next)) + (//.path/bind next)]]) + )))) + sequential (: (Scenario Path) + (.function (recur offset arity next) + (do random.monad + [[next [patternE patternA]] (pattern offset arity next) + [next [bodyE bodyA]] (..reference offset arity next)] + (wrap [next + [(//.path/seq patternE (//.path/then bodyE)) + (//.path/seq patternA (//.path/then bodyA))]]))))] + (.function (recur offset arity next) + (do random.monad + [[next [leftE leftA]] (sequential offset arity next) + [next [rightE rightA]] (sequential offset arity next)] + (wrap [next + [(//.path/alt leftE rightE) + (//.path/alt leftA rightA)]]))))) + +(def: (branch offset arity next) + (Scenario Synthesis) + (let [random-member (: (Random Member) + (random.or random.nat + random.nat))] + ($_ random.either + ($_ random.either + (do {@ random.monad} + [[next [inputE inputA]] (..reference offset arity next) + [next [bodyE bodyA]] (..reference offset arity next)] + (wrap [next + [(//.branch/let [inputE (/.register-optimization offset next) bodyE]) + (//.branch/let [inputA next bodyA])]])) + (do {@ random.monad} + [[next [testE testA]] (..reference offset arity next) + [next [thenE thenA]] (..reference offset arity next) + [next [elseE elseA]] (..reference offset arity next)] + (wrap [next + [(//.branch/if [testE thenE elseE]) + (//.branch/if [testA thenA elseA])]]))) + ($_ random.either + (do {@ random.monad} + [[next [recordE recordA]] (..reference offset arity next) + path-length (:: @ map (|>> (n.% 5) inc) random.nat) + path (random.list path-length random-member)] + (wrap [next + [(//.branch/get [path recordE]) + (//.branch/get [path recordA])]])) + (do {@ random.monad} + [[next [inputE inputA]] (..reference offset arity next) + [next [pathE pathA]] (..path offset arity next)] + (wrap [next + [(//.branch/case [inputE pathE]) + (//.branch/case [inputA pathA])]]))) + ))) + +(def: (loop offset arity next) + (Scenario Synthesis) + ($_ random.either + (do random.monad + [[next [firstE firstA]] (..reference offset arity next) + [next [secondE secondA]] (..reference offset arity next) + [next [iterationE iterationA]] (..reference offset arity next)] + (wrap [next + [(//.loop/scope + {#//.start (/.register-optimization offset next) + #//.inits (list firstE secondE) + #//.iteration iterationE}) + (//.loop/scope + {#//.start next + #//.inits (list firstA secondA) + #//.iteration iterationA})]])) + )) + +(def: (function offset arity next) + (Scenario Synthesis) + ($_ random.either + (do {@ random.monad} + [[next [firstE firstA]] (..variable offset arity next) + [next [secondE secondA]] (..variable offset arity next) + arity (:: @ map (n.max 1) random.nat) + [next [bodyE bodyA]] (..primitive 0 arity next)] + (wrap [next + [(//.function/abstraction + {#//.environment (list firstE secondE) + #//.arity arity + #//.body bodyE}) + (//.function/abstraction + {#//.environment (list firstA secondA) + #//.arity arity + #//.body bodyA})]])) + )) + +(def: (control offset arity next) + (Scenario Synthesis) + ($_ random.either + (..branch offset arity next) + (..loop offset arity next) + (..function offset arity next) + )) + +(def: (extension offset arity next) + (Scenario Synthesis) + (do random.monad + [name (random.unicode 10) + [next [firstE firstA]] (..reference offset arity next) + [next [secondE secondA]] (..reference offset arity next) + [next [thirdE thirdA]] (..reference offset arity next)] + (wrap [next + [(#//.Extension name (list firstE secondE thirdE)) + (#//.Extension name (list firstA secondA thirdA))]]))) + +(def: (scenario offset arity next) + (Scenario Synthesis) + ($_ random.either + (..primitive offset arity next) + (..structure offset arity next) + (..reference offset arity next) + (..control offset arity next) + (..extension offset arity next) + )) + +(def: #export test + Test + (<| (_.covering /._) + ($_ _.and + (do {@ random.monad} + [expected-offset (:: @ map (|>> (n.% 5) (n.+ 2)) random.nat) + arity (:: @ map (|>> (n.% 5) inc) random.nat) + expected-inits (|> random.nat + (:: @ map (|>> .i64 //.i64)) + (random.list arity)) + [_ [expected iteration]] (..scenario expected-offset arity 0)] + (_.cover [/.Transform /.optimization /.register-optimization] + (case (/.optimization expected-offset expected-inits + {#//.environment (|> expected-offset + list.indices + (list@map (|>> #variable.Local))) + #//.arity arity + #//.body iteration}) + (^ (#.Some (//.loop/scope [actual-offset actual-inits + actual]))) + (and (n.= expected-offset + actual-offset) + (:: (list.equivalence //.equivalence) = + expected-inits + actual-inits) + (:: //.equivalence = expected actual)) + + _ + false))) + ))) -- cgit v1.2.3