1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-14 17:32:00 +03:00
juvix/test/Compilation/Base.hs

51 lines
2.0 KiB
Haskell
Raw Normal View History

module Compilation.Base where
import Base
Add compilation of complex pattern matching to case (#1824) This PR adds the `match-to-case` Core transformation. This transforms pattern matching nodes to a sequence of case and let nodes. ## High level description Each branch of the match is compiled to a lambda. In the combined match Each branch of the match is compiled to a lambda. These lambdas are combined in nested lets and each lambda is called in turn as each branch gets checked. The lambda corresponding to the first branch gets called first, if the pattern match in the branch fails, the lambda corresponding to the next branch is called and so on. If no branches match then a lambda is called which returns a fail node. Conceptually: <table> <tr> <td> Core </td> <td> Transformed </td> </tr> <tr> <td> ``` match v1 .. vn { b1 b2 ... bk } ``` </td> <td> ``` λ let c0 := λ FAIL in let ck := λ {...} in ... let c1 := λ {...} in c1 v1 ... vn ``` </td> </tr> </table> The patterns on each branch are compiled to either let bindings (pattern binders) or case expressions (constructor patterns). Auxillary bindings are added in the case of nested constructor patterns. The default branch in each case expression has a call to the lambda corresponding to the next branch of the match. This is because the default branch is reached if the pattern match fails. <table> <tr> <td> Pattern match </td> <td> Transformed </td> </tr> <tr> <td> ``` suc (suc n) ↦ n ``` </td> <td> ``` case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } ``` </td> </tr> </table> The body of each branch is wrapped in let bindings so that the indicies of bound variables in the body point to the correct variables in the compiled expression. This is necessary because the auxiliary bindings added for nested constructor patterns will cause the original indicies to be offset. Finally, the free variables in the match branch body need to be shifted by all the bindings we've added as part of the compilation. ## Examples ### Single wildcard <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f _ := 1; ``` </td> <td> ``` λ? match ?$0 with { _ω309 ↦ ? 1 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let _ω309 := ?$0 in let _ω309 := ?$0 in 1 in ?$0 ?$2 ``` </td> </tr> </table> ### Single binder <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f n := n; ``` </td> <td> ``` λ? match ?$0 with { n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let n := ?$0 in let n := ?$0 in n$0 in ?$0 ?$2 ``` </td> </tr> </table> ### Single Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Nested Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc (suc n)) := n; ``` </td> <td> ``` λ? match ?$0 with { suc (suc n) ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Multiple Branches <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; f zero := 0; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0; zero ↦ ? 0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { zero := ? 0; _ := ?$1 ?$0 } in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$3 ``` </td> </tr> </table> ### Nested case with captured variable <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat -> Nat; f n m := case m | suc k := n + k; ``` </td> <td> ``` f = λ? λ? match ?$1, ?$0 with { n, m ↦ match m$0 with { suc k ↦ + n$2 k$0 } } ``` </td> <td> ``` λ? λ? let ? := λ? λ? fail "Non-exhaustive patterns" in let ? := λ? λ? let n := ?$1 in let m := ?$1 in let n := ?$1 in let m := ?$1 in let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc k := let k := ?$0 in let k := ?$0 in + n$6 k$0; _ := ?$1 ?$0 } in ?$0 m$2 in ?$0 ?$3 ?$2 ``` </td> </tr> </table> ## Testing The `tests/Compilation/positive` tests are run up to the Core evaluator with `match-to-case` and `nat-to-int` transformations on Core turned on. --------- Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-02-15 13:30:12 +03:00
import Core.Eval.Base
import Data.HashMap.Strict qualified as HashMap
Add compilation of complex pattern matching to case (#1824) This PR adds the `match-to-case` Core transformation. This transforms pattern matching nodes to a sequence of case and let nodes. ## High level description Each branch of the match is compiled to a lambda. In the combined match Each branch of the match is compiled to a lambda. These lambdas are combined in nested lets and each lambda is called in turn as each branch gets checked. The lambda corresponding to the first branch gets called first, if the pattern match in the branch fails, the lambda corresponding to the next branch is called and so on. If no branches match then a lambda is called which returns a fail node. Conceptually: <table> <tr> <td> Core </td> <td> Transformed </td> </tr> <tr> <td> ``` match v1 .. vn { b1 b2 ... bk } ``` </td> <td> ``` λ let c0 := λ FAIL in let ck := λ {...} in ... let c1 := λ {...} in c1 v1 ... vn ``` </td> </tr> </table> The patterns on each branch are compiled to either let bindings (pattern binders) or case expressions (constructor patterns). Auxillary bindings are added in the case of nested constructor patterns. The default branch in each case expression has a call to the lambda corresponding to the next branch of the match. This is because the default branch is reached if the pattern match fails. <table> <tr> <td> Pattern match </td> <td> Transformed </td> </tr> <tr> <td> ``` suc (suc n) ↦ n ``` </td> <td> ``` case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } ``` </td> </tr> </table> The body of each branch is wrapped in let bindings so that the indicies of bound variables in the body point to the correct variables in the compiled expression. This is necessary because the auxiliary bindings added for nested constructor patterns will cause the original indicies to be offset. Finally, the free variables in the match branch body need to be shifted by all the bindings we've added as part of the compilation. ## Examples ### Single wildcard <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f _ := 1; ``` </td> <td> ``` λ? match ?$0 with { _ω309 ↦ ? 1 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let _ω309 := ?$0 in let _ω309 := ?$0 in 1 in ?$0 ?$2 ``` </td> </tr> </table> ### Single binder <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f n := n; ``` </td> <td> ``` λ? match ?$0 with { n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let n := ?$0 in let n := ?$0 in n$0 in ?$0 ?$2 ``` </td> </tr> </table> ### Single Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Nested Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc (suc n)) := n; ``` </td> <td> ``` λ? match ?$0 with { suc (suc n) ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Multiple Branches <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; f zero := 0; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0; zero ↦ ? 0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { zero := ? 0; _ := ?$1 ?$0 } in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$3 ``` </td> </tr> </table> ### Nested case with captured variable <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat -> Nat; f n m := case m | suc k := n + k; ``` </td> <td> ``` f = λ? λ? match ?$1, ?$0 with { n, m ↦ match m$0 with { suc k ↦ + n$2 k$0 } } ``` </td> <td> ``` λ? λ? let ? := λ? λ? fail "Non-exhaustive patterns" in let ? := λ? λ? let n := ?$1 in let m := ?$1 in let n := ?$1 in let m := ?$1 in let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc k := let k := ?$0 in let k := ?$0 in + n$6 k$0; _ := ?$1 ?$0 } in ?$0 m$2 in ?$0 ?$3 ?$2 ``` </td> </tr> </table> ## Testing The `tests/Compilation/positive` tests are run up to the Core evaluator with `match-to-case` and `nat-to-int` transformations on Core turned on. --------- Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-02-15 13:30:12 +03:00
import Data.Text.IO qualified as TIO
import Juvix.Compiler.Builtins (iniState)
import Juvix.Compiler.Core.Data.InfoTable qualified as Core
Add compilation of complex pattern matching to case (#1824) This PR adds the `match-to-case` Core transformation. This transforms pattern matching nodes to a sequence of case and let nodes. ## High level description Each branch of the match is compiled to a lambda. In the combined match Each branch of the match is compiled to a lambda. These lambdas are combined in nested lets and each lambda is called in turn as each branch gets checked. The lambda corresponding to the first branch gets called first, if the pattern match in the branch fails, the lambda corresponding to the next branch is called and so on. If no branches match then a lambda is called which returns a fail node. Conceptually: <table> <tr> <td> Core </td> <td> Transformed </td> </tr> <tr> <td> ``` match v1 .. vn { b1 b2 ... bk } ``` </td> <td> ``` λ let c0 := λ FAIL in let ck := λ {...} in ... let c1 := λ {...} in c1 v1 ... vn ``` </td> </tr> </table> The patterns on each branch are compiled to either let bindings (pattern binders) or case expressions (constructor patterns). Auxillary bindings are added in the case of nested constructor patterns. The default branch in each case expression has a call to the lambda corresponding to the next branch of the match. This is because the default branch is reached if the pattern match fails. <table> <tr> <td> Pattern match </td> <td> Transformed </td> </tr> <tr> <td> ``` suc (suc n) ↦ n ``` </td> <td> ``` case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } ``` </td> </tr> </table> The body of each branch is wrapped in let bindings so that the indicies of bound variables in the body point to the correct variables in the compiled expression. This is necessary because the auxiliary bindings added for nested constructor patterns will cause the original indicies to be offset. Finally, the free variables in the match branch body need to be shifted by all the bindings we've added as part of the compilation. ## Examples ### Single wildcard <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f _ := 1; ``` </td> <td> ``` λ? match ?$0 with { _ω309 ↦ ? 1 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let _ω309 := ?$0 in let _ω309 := ?$0 in 1 in ?$0 ?$2 ``` </td> </tr> </table> ### Single binder <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f n := n; ``` </td> <td> ``` λ? match ?$0 with { n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let n := ?$0 in let n := ?$0 in n$0 in ?$0 ?$2 ``` </td> </tr> </table> ### Single Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Nested Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc (suc n)) := n; ``` </td> <td> ``` λ? match ?$0 with { suc (suc n) ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Multiple Branches <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; f zero := 0; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0; zero ↦ ? 0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { zero := ? 0; _ := ?$1 ?$0 } in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$3 ``` </td> </tr> </table> ### Nested case with captured variable <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat -> Nat; f n m := case m | suc k := n + k; ``` </td> <td> ``` f = λ? λ? match ?$1, ?$0 with { n, m ↦ match m$0 with { suc k ↦ + n$2 k$0 } } ``` </td> <td> ``` λ? λ? let ? := λ? λ? fail "Non-exhaustive patterns" in let ? := λ? λ? let n := ?$1 in let m := ?$1 in let n := ?$1 in let m := ?$1 in let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc k := let k := ?$0 in let k := ?$0 in + n$6 k$0; _ := ?$1 ?$0 } in ?$0 m$2 in ?$0 ?$3 ?$2 ``` </td> </tr> </table> ## Testing The `tests/Compilation/positive` tests are run up to the Core evaluator with `match-to-case` and `nat-to-int` transformations on Core turned on. --------- Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-02-15 13:30:12 +03:00
import Juvix.Compiler.Core.Extra
import Juvix.Compiler.Core.Info qualified as Info
import Juvix.Compiler.Core.Info.NoDisplayInfo
import Juvix.Compiler.Core.Pipeline qualified as Core
import Juvix.Compiler.Core.Pretty
import Juvix.Compiler.Core.Translation.FromInternal.Data qualified as Core
import Juvix.Compiler.Pipeline
compileAssertion ::
Path Abs File ->
Path Abs File ->
(String -> IO ()) ->
Assertion
Add compilation of complex pattern matching to case (#1824) This PR adds the `match-to-case` Core transformation. This transforms pattern matching nodes to a sequence of case and let nodes. ## High level description Each branch of the match is compiled to a lambda. In the combined match Each branch of the match is compiled to a lambda. These lambdas are combined in nested lets and each lambda is called in turn as each branch gets checked. The lambda corresponding to the first branch gets called first, if the pattern match in the branch fails, the lambda corresponding to the next branch is called and so on. If no branches match then a lambda is called which returns a fail node. Conceptually: <table> <tr> <td> Core </td> <td> Transformed </td> </tr> <tr> <td> ``` match v1 .. vn { b1 b2 ... bk } ``` </td> <td> ``` λ let c0 := λ FAIL in let ck := λ {...} in ... let c1 := λ {...} in c1 v1 ... vn ``` </td> </tr> </table> The patterns on each branch are compiled to either let bindings (pattern binders) or case expressions (constructor patterns). Auxillary bindings are added in the case of nested constructor patterns. The default branch in each case expression has a call to the lambda corresponding to the next branch of the match. This is because the default branch is reached if the pattern match fails. <table> <tr> <td> Pattern match </td> <td> Transformed </td> </tr> <tr> <td> ``` suc (suc n) ↦ n ``` </td> <td> ``` case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } ``` </td> </tr> </table> The body of each branch is wrapped in let bindings so that the indicies of bound variables in the body point to the correct variables in the compiled expression. This is necessary because the auxiliary bindings added for nested constructor patterns will cause the original indicies to be offset. Finally, the free variables in the match branch body need to be shifted by all the bindings we've added as part of the compilation. ## Examples ### Single wildcard <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f _ := 1; ``` </td> <td> ``` λ? match ?$0 with { _ω309 ↦ ? 1 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let _ω309 := ?$0 in let _ω309 := ?$0 in 1 in ?$0 ?$2 ``` </td> </tr> </table> ### Single binder <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f n := n; ``` </td> <td> ``` λ? match ?$0 with { n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let n := ?$0 in let n := ?$0 in n$0 in ?$0 ?$2 ``` </td> </tr> </table> ### Single Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Nested Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc (suc n)) := n; ``` </td> <td> ``` λ? match ?$0 with { suc (suc n) ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Multiple Branches <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; f zero := 0; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0; zero ↦ ? 0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { zero := ? 0; _ := ?$1 ?$0 } in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$3 ``` </td> </tr> </table> ### Nested case with captured variable <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat -> Nat; f n m := case m | suc k := n + k; ``` </td> <td> ``` f = λ? λ? match ?$1, ?$0 with { n, m ↦ match m$0 with { suc k ↦ + n$2 k$0 } } ``` </td> <td> ``` λ? λ? let ? := λ? λ? fail "Non-exhaustive patterns" in let ? := λ? λ? let n := ?$1 in let m := ?$1 in let n := ?$1 in let m := ?$1 in let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc k := let k := ?$0 in let k := ?$0 in + n$6 k$0; _ := ?$1 ?$0 } in ?$0 m$2 in ?$0 ?$3 ?$2 ``` </td> </tr> </table> ## Testing The `tests/Compilation/positive` tests are run up to the Core evaluator with `match-to-case` and `nat-to-int` transformations on Core turned on. --------- Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-02-15 13:30:12 +03:00
compileAssertion mainFile expectedFile step = do
step "Translate to JuvixCore"
cwd <- getCurrentDir
let entryPoint = defaultEntryPoint cwd mainFile
Add compilation of complex pattern matching to case (#1824) This PR adds the `match-to-case` Core transformation. This transforms pattern matching nodes to a sequence of case and let nodes. ## High level description Each branch of the match is compiled to a lambda. In the combined match Each branch of the match is compiled to a lambda. These lambdas are combined in nested lets and each lambda is called in turn as each branch gets checked. The lambda corresponding to the first branch gets called first, if the pattern match in the branch fails, the lambda corresponding to the next branch is called and so on. If no branches match then a lambda is called which returns a fail node. Conceptually: <table> <tr> <td> Core </td> <td> Transformed </td> </tr> <tr> <td> ``` match v1 .. vn { b1 b2 ... bk } ``` </td> <td> ``` λ let c0 := λ FAIL in let ck := λ {...} in ... let c1 := λ {...} in c1 v1 ... vn ``` </td> </tr> </table> The patterns on each branch are compiled to either let bindings (pattern binders) or case expressions (constructor patterns). Auxillary bindings are added in the case of nested constructor patterns. The default branch in each case expression has a call to the lambda corresponding to the next branch of the match. This is because the default branch is reached if the pattern match fails. <table> <tr> <td> Pattern match </td> <td> Transformed </td> </tr> <tr> <td> ``` suc (suc n) ↦ n ``` </td> <td> ``` case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } ``` </td> </tr> </table> The body of each branch is wrapped in let bindings so that the indicies of bound variables in the body point to the correct variables in the compiled expression. This is necessary because the auxiliary bindings added for nested constructor patterns will cause the original indicies to be offset. Finally, the free variables in the match branch body need to be shifted by all the bindings we've added as part of the compilation. ## Examples ### Single wildcard <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f _ := 1; ``` </td> <td> ``` λ? match ?$0 with { _ω309 ↦ ? 1 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let _ω309 := ?$0 in let _ω309 := ?$0 in 1 in ?$0 ?$2 ``` </td> </tr> </table> ### Single binder <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f n := n; ``` </td> <td> ``` λ? match ?$0 with { n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let n := ?$0 in let n := ?$0 in n$0 in ?$0 ?$2 ``` </td> </tr> </table> ### Single Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Nested Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc (suc n)) := n; ``` </td> <td> ``` λ? match ?$0 with { suc (suc n) ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Multiple Branches <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; f zero := 0; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0; zero ↦ ? 0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { zero := ? 0; _ := ?$1 ?$0 } in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$3 ``` </td> </tr> </table> ### Nested case with captured variable <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat -> Nat; f n m := case m | suc k := n + k; ``` </td> <td> ``` f = λ? λ? match ?$1, ?$0 with { n, m ↦ match m$0 with { suc k ↦ + n$2 k$0 } } ``` </td> <td> ``` λ? λ? let ? := λ? λ? fail "Non-exhaustive patterns" in let ? := λ? λ? let n := ?$1 in let m := ?$1 in let n := ?$1 in let m := ?$1 in let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc k := let k := ?$0 in let k := ?$0 in + n$6 k$0; _ := ?$1 ?$0 } in ?$0 m$2 in ?$0 ?$3 ?$2 ``` </td> </tr> </table> ## Testing The `tests/Compilation/positive` tests are run up to the Core evaluator with `match-to-case` and `nat-to-int` transformations on Core turned on. --------- Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-02-15 13:30:12 +03:00
tab' <- (^. Core.coreResultTable) . snd <$> runIO' iniState entryPoint upToCore
let tab = Core.toEval tab'
case (tab ^. Core.infoMain) >>= ((tab ^. Core.identContext) HashMap.!?) of
Add compilation of complex pattern matching to case (#1824) This PR adds the `match-to-case` Core transformation. This transforms pattern matching nodes to a sequence of case and let nodes. ## High level description Each branch of the match is compiled to a lambda. In the combined match Each branch of the match is compiled to a lambda. These lambdas are combined in nested lets and each lambda is called in turn as each branch gets checked. The lambda corresponding to the first branch gets called first, if the pattern match in the branch fails, the lambda corresponding to the next branch is called and so on. If no branches match then a lambda is called which returns a fail node. Conceptually: <table> <tr> <td> Core </td> <td> Transformed </td> </tr> <tr> <td> ``` match v1 .. vn { b1 b2 ... bk } ``` </td> <td> ``` λ let c0 := λ FAIL in let ck := λ {...} in ... let c1 := λ {...} in c1 v1 ... vn ``` </td> </tr> </table> The patterns on each branch are compiled to either let bindings (pattern binders) or case expressions (constructor patterns). Auxillary bindings are added in the case of nested constructor patterns. The default branch in each case expression has a call to the lambda corresponding to the next branch of the match. This is because the default branch is reached if the pattern match fails. <table> <tr> <td> Pattern match </td> <td> Transformed </td> </tr> <tr> <td> ``` suc (suc n) ↦ n ``` </td> <td> ``` case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } ``` </td> </tr> </table> The body of each branch is wrapped in let bindings so that the indicies of bound variables in the body point to the correct variables in the compiled expression. This is necessary because the auxiliary bindings added for nested constructor patterns will cause the original indicies to be offset. Finally, the free variables in the match branch body need to be shifted by all the bindings we've added as part of the compilation. ## Examples ### Single wildcard <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f _ := 1; ``` </td> <td> ``` λ? match ?$0 with { _ω309 ↦ ? 1 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let _ω309 := ?$0 in let _ω309 := ?$0 in 1 in ?$0 ?$2 ``` </td> </tr> </table> ### Single binder <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f n := n; ``` </td> <td> ``` λ? match ?$0 with { n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? let n := ?$0 in let n := ?$0 in n$0 in ?$0 ?$2 ``` </td> </tr> </table> ### Single Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Nested Constructor <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc (suc n)) := n; ``` </td> <td> ``` λ? match ?$0 with { suc (suc n) ↦ n$0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc arg_8 := case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$2 ?$1 }; _ := ?$1 ?$0 } in ?$0 ?$2 ``` </td> </tr> </table> ### Multiple Branches <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat; f (suc n) := n; f zero := 0; ``` </td> <td> ``` λ? match ?$0 with { suc n ↦ n$0; zero ↦ ? 0 } ``` </td> <td> ``` λ? let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { zero := ? 0; _ := ?$1 ?$0 } in let ? := λ? case ?$0 of { suc n := let n := ?$0 in let n := ?$0 in n$0; _ := ?$1 ?$0 } in ?$0 ?$3 ``` </td> </tr> </table> ### Nested case with captured variable <table> <tr> <td> Juvix </td> <td> Core </td> <td> Transformed Core </td> </tr> <tr> <td> ``` f : Nat -> Nat -> Nat; f n m := case m | suc k := n + k; ``` </td> <td> ``` f = λ? λ? match ?$1, ?$0 with { n, m ↦ match m$0 with { suc k ↦ + n$2 k$0 } } ``` </td> <td> ``` λ? λ? let ? := λ? λ? fail "Non-exhaustive patterns" in let ? := λ? λ? let n := ?$1 in let m := ?$1 in let n := ?$1 in let m := ?$1 in let ? := λ? fail "Non-exhaustive patterns" in let ? := λ? case ?$0 of { suc k := let k := ?$0 in let k := ?$0 in + n$6 k$0; _ := ?$1 ?$0 } in ?$0 m$2 in ?$0 ?$3 ?$2 ``` </td> </tr> </table> ## Testing The `tests/Compilation/positive` tests are run up to the Core evaluator with `match-to-case` and `nat-to-int` transformations on Core turned on. --------- Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2023-02-15 13:30:12 +03:00
Just node -> do
withTempDir'
( \dirPath -> do
let outputFile = dirPath <//> $(mkRelFile "out.out")
hout <- openFile (toFilePath outputFile) WriteMode
step "Evaluate"
r' <- doEval mainFile hout tab node
case r' of
Left err -> do
hClose hout
assertFailure (show (pretty err))
Right value -> do
unless
(Info.member kNoDisplayInfo (getInfo value))
(hPutStrLn hout (ppPrint value))
hClose hout
actualOutput <- TIO.readFile (toFilePath outputFile)
step "Compare expected and actual program output"
expected <- TIO.readFile (toFilePath expectedFile)
assertEqDiffText ("Check: EVAL output = " <> toFilePath expectedFile) actualOutput expected
)
Nothing -> assertFailure ("No main function registered in: " <> toFilePath mainFile)