diff --git a/.gitignore b/.gitignore index 53a8938..fe70057 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,7 @@ tags # temp files temp/ + +# editors +MLStyle.jl.iml +.vscode/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 45e0c96..4ac33c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ os: julia: - 1.0 - 1.1 + - 1.4 - nightly matrix: allow_failures: diff --git a/Project.toml b/Project.toml index 784aa71..d73a1ef 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MLStyle" uuid = "d8e11817-5142-5d16-987a-aa16d5891078" authors = ["thautwarm "] -version = "0.3.1" +version = "0.4.0" [extras] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" diff --git a/README.md b/README.md index 301ef37..a4d5b8e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ MLStyle.jl [![Docs](https://img.shields.io/badge/docs-latest-purple.svg)](https://thautwarm.github.io/MLStyle.jl/latest/) [![Join the chat at https://gitter.im/MLStyle-jl/community](https://badges.gitter.im/MLStyle-jl/community.svg)](https://gitter.im/MLStyle-jl/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +This README and the documentations are so far for v0.3 and not up-to-date yet. We're actively working on this. + ## Index - [What is MLStyle.jl](#what-is-mlstylejl) diff --git a/docs/make.jl b/docs/make.jl index da85c1a..27b4c31 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -7,18 +7,14 @@ makedocs( MLStyle.MatchCore, MLStyle.DataType, MLStyle.Err, - MLStyle.Extension, - MLStyle.Infras, MLStyle.Pervasives, + MLStyle.Record, # standard patterns - MLStyle.TypeVarDecons, MLStyle.Active, MLStyle.Uncomprehensions, MLStyle.LambdaCases, - MLStyle.WhenCases, - - MLStyle.Render + MLStyle.WhenCases ], clean = false, format = :html, @@ -29,11 +25,10 @@ makedocs( "Home" => "index.md", "Syntax" => Any[ "syntax/adt.md", + "syntax/switch.md", "syntax/pattern.md", "syntax/pattern-function.md", "syntax/when.md", - "syntax/extension.md", - "syntax/qualifier.md" ], "Tutorials" => Any[ "tutorials/capture.md", diff --git a/docs/src/index.md b/docs/src/index.md index 1caedb9..a1c0ac8 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -7,6 +7,12 @@ MLStyle.jl [![Docs](https://img.shields.io/badge/docs-latest-purple.svg)](https://thautwarm.github.io/MLStyle.jl/latest/) [![Join the chat at https://gitter.im/MLStyle-jl/community](https://badges.gitter.im/MLStyle-jl/community.svg)](https://gitter.im/MLStyle-jl/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +***This is the documentation for unreleased v0.4, which has a lot of improvements, i.e. "changes".*** + +***You may now check docs of v0.3.1.*** + + ## What is MLStyle.jl? MLStyle.jl is a Julia package that provides multiple productivity tools from ML ([Meta Language](https://en.wikipedia.org/wiki/ML_(programming_language))) like [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching) which is statically generated and extensible, ADTs/GADTs ([Algebraic Data Type](https://en.wikipedia.org/wiki/Algebraic_data_type), [Generalized Algebraic Data Type](https://en.wikipedia.org/wiki/Generalized_algebraic_data_type)) and [Active Patterns](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns). diff --git a/docs/src/syntax/adt.md b/docs/src/syntax/adt.md index a550a0d..c67bf8a 100644 --- a/docs/src/syntax/adt.md +++ b/docs/src/syntax/adt.md @@ -2,51 +2,53 @@ Algebraic Data Types ============================== -Syntax +Cheat Sheet ----------------- -``` - - a = a (',' a)* - = %Uppercase identifier% - = %Lowercase identifier% - = %Uppercase identifier% - = %Uppercase identifier% - = %Uppercase identifier% - = [ '{' '}' ] - = %Uppercase identifier% - - = - '@data' ['public' | 'internal' | 'visible' 'in' ] 'begin' +Cheat sheet for regular ADT definitions: - ([{}] ( - | | :: )> - ))* - - 'end' - - = - '@data' ['public' | 'internal' | 'visible' 'in' ] 'begin' +```julia +@data A <: B begin + C1 # is an enum(**WIP**) + + # similar to C1 but cannot match without a call + C2() + + # the capitalized means types(field names are "_1", "_2", ...) + # C3(1, "2")._1 == 1 + C3(Int, String) + + C4(::Int, ::String) # "::" means types + + # the lowercase means field names + # C5(:ss, 1.0).a == :ss + C5(a, b) + + C6(a::Int, b::Vector{<:AbstractString}) +end +``` - ([{}] '::' - ( '(' - ( | | :: )>) - ')' - | - | - ) - '=>' ['where' '{' '}'] - )* +Cheat sheet for GADT definitions: - 'end' +```julia +@data Ab{T} <: AB begin + + C1{X, Y} :: Ab{X} # is an enum(**WIP**) + C2 :: () => Ab{Int} + # where is for inference, the clauses must be assignments + C3{A<:Number, B} :: (a::A, b::Symbol) => Ab{B} where {B = Type{A}} + # C3(1, :a) :: C3{Int, Tuple{Int}} + # C3(1, :a) :: Ab{Int, Tuple{Int}} +end ``` -Examples: +Examples +------------------------- ```julia -@data internal A begin +@data A begin A1(Int, Int) A2(a :: Int, b :: Int) A3(a, b) # equals to `A3(a::Any, b::Any)` @@ -57,32 +59,25 @@ end B2(a :: T) end -@data visible in MyModule C{T} begin +@data C{T} begin C1(T) C2{A} :: Vector{A} => C{A} end abstract type DD end -@data visible in [Main, Base, Core] D{T} <: DD begin - D1 :: Int => D{T} where T # implicit type vars +some_type_to_int(x::Type{Int}) = 1 +some_type_to_int(x::Type{<:Tuple}) = 2 + +@data D{T} <: DD begin + D1{T} :: Int => D{T} D2{A, B} :: (A, B, Int) => D{Tuple{A, B}} - D3{A} :: A => D{Array{A, N}} where N # implicit type vars + D3{A, N} :: A => D{Array{A, N}} where {N = some_type_to_int(A)} end +# z :: D3{Int64,1}(10) = D3(10) :: D{Array{Int64,1}} ``` -Qualifier ----------------------- - - -There are 3 default qualifiers for ADT definition: - -- `internal`: The pattern created by the ADT can only be used in the module it's defined in. -- `public`: If the constructor is imported into current module, the corresponding pattern will be available. -- `visible in [mod...]`: Define a set of modules where the pattern is available. - - -Example: Describe arithmetic operations --------------------------------------- +Example: Modeling Arithmetic Operations +---------------------------------------------- ```julia using MLStyle @@ -104,8 +99,8 @@ eval_arith(arith :: Arith) = let wrap_op(op) = (a, b) -> op(eval_arith(a), eval_arith(b)), (+, -, *, /) = map(wrap_op, (+, -, *, /)) @match arith begin - Number(v) => v - Minus(fst, snd) => fst - snd + Number(v) => v + Minus(fst, snd) => fst - snd Mult(fst, snd) => fst * snd Divide(fst, snd) => fst / snd end @@ -122,182 +117,15 @@ eval_arith( -Generalized ADT --------------------------- - - - -Note that, for GADTs would use `where` syntax as a pattern, it means that you cannot -use GADTs and your custom `where` patterns at the same time. To resolve this, we introduce -the extension system like Haskell here. - -Since that you can define your own `where` pattern and export it to any modules. -Given an arbitrary Julia module, if you don't use `@use GADT` to enable GADT extensions and, -your own `where` pattern just works here. - - -Here's a simple interpreter implemented using GADTs. - -Firstly, enable GADT extension. - -```julia -using MLStyle -@use GADT -``` - -Then define the function type. - -```julia -import Base: convert - -struct Fun{T, R} - fn :: Function -end - -function (typed_fn :: Fun{T, R})(arg :: T) :: R where {T, R} - typed_fn.fn(arg) -end - -function convert(::Type{Fun{T, R}}, fn :: Function) where {T, R} - Fun{T, R}(fn) -end - -function convert(::Type{Fun{T, R}}, fn :: Fun{C, D}) where{T, R, C <: T, D <: R} - Fun{T, R}(fn.fn) -end - -⇒(::Type{A}, ::Type{B}) where {A, B} = Fun{A, B} -``` - -And now let's define the operators of our abstract machine. - -```julia - -@data public Exp{T} begin - - # The symbol refers to some variable in current context. - Sym{A} :: Symbol => Exp{A} - - # Value. - Val{A} :: A => Exp{A} - - # Function application. - App{A, B, A_ <: A} :: (Exp{Fun{A, B}}, Exp{A_}) => Exp{B} - - # Lambda/Anonymous function. - Lam{A, B} :: (Symbol, Exp{B}) => Exp{Fun{A, B}} - - # If expression - If{A} :: (Exp{Bool}, Exp{A}, Exp{A}) => Exp{A} -end -``` - -To make function abstractions, we need a `substitute` operation. - -```julia - -""" -e.g: substitute(some_exp, :a => another_exp) -""" -function substitute(template :: Exp{T}, pair :: Tuple{Symbol, Exp{G}}) where {T, G} - (sym, exp) = pair - @match template begin - Sym(&sym) => exp - Val(_) => template - App(f, a) => App(substitute(f, pair), substitute(a, pair)) :: Exp{T} - Lam(&sym, exp) => template - If(cond, exp1, exp2) => - let (cond, exp1, exp2) = map(substitute, (cond, exp1, exp2)) - If(cond, exp1, exp2) :: Exp{T} - end - end -end -``` - -Then we could write how to execute our abstract machine. - -```julia -function eval_exp(exp :: Exp{T}, ctx :: Dict{Symbol, Any}) where T - @match exp begin - Sym(a) => (ctx[a] :: T, ctx) - Val(a :: T) => (a, ctx) - App{A, T, A_}(f :: Exp{Fun{A, T}}, arg :: Exp{A_}) where {A, A_ <: A} => - let (f, ctx) = eval_exp(f, ctx), - (arg, ctx) = eval_exp(arg, ctx) - (f(arg), ctx) - end - Lam{A, B}(sym, exp::Exp{B}) where {A, B} => - let f(x :: A) = begin - A - eval_exp(substitute(exp, sym => Val(x)), ctx)[1] - end - - (f, ctx) - end - If(cond, exp1, exp2) => - let (cond, ctx) = eval_exp(cond, ctx) - eval_exp(cond ? exp1 : exp2, ctx) - end - end -end -``` - -This `eval_exp` takes 2 arguments, one of which is an `Exp{T}`, while another is the store(you can regard it as the scope), -the return is a tuple, the first of which is a value typed `T` and the second is the new store after the execution. - -Following codes are about how to use this abstract machine. - -```julia -add = Val{Number ⇒ Number ⇒ Number}(x -> y -> x + y) -sub = Val{Number ⇒ Number ⇒ Number}(x -> y -> x - y) -gt = Val{Number ⇒ Number ⇒ Bool}(x -> y -> x > y) -ctx = Dict{Symbol, Any}() - -@assert 3 == eval_exp(App(App(add, Val(1)), Val(2)), ctx)[1] -@assert -1 == eval_exp(App(App(sub, Val(1)), Val(2)), ctx)[1] -@assert 1 == eval_exp( - If( - App(App(gt, Sym{Int}(:x)), Sym{Int}(:y)), - App(App(sub, Sym{Int}(:x)), Sym{Int}(:y)), - App(App(sub, Sym{Int}(:y)), Sym{Int}(:x)) - ), Dict{Symbol, Any}(:x => 1, :y => 2))[1] - -``` - - -Implicit Type Variables of Generalized ADT +About Type Parameters ---------------------------------------------------- +`where` is used for type parameter introduction. -Sometimes you might want this: - -```julia -@use GADT - -@data A{T} begin - A1 :: Int => A{T} where T -end -``` -It means that for all `T`, we have `A{T} >: A1`, where `A1` is a case class and could be used as a constructor. - -You can work with them in this way: +Following 2 patterns are equivalent: ```julia -function string_A() :: A{String} - A1(2) -end - -@assert String == @match string_A() begin - A{T} where T => T -end -``` - -Currently, there're several limitations with implicit type variables, say, you're not expected to use implicit type variables in -the argument types of constructors, like: - -```julia -@data A{T} begin - A1 :: T => A{T} where T # NOT EXPECTED! -end +A{T1...}(T2...) where {T3...} +A{T1...}(T2...) :: A{T1...} where {T3...} ``` -It's possible to achieve more flexible implicit type variables, but it's quite difficult for such a package without statically type checking. \ No newline at end of file +Check [Advanced Type Pattern](https://thautwarm.github.io/MLStyle.jl/latest/syntax/pattern/#Advanced-Type-Pattern-1) for more about `where` use in matching. diff --git a/docs/src/syntax/extension.md b/docs/src/syntax/extension.md deleted file mode 100644 index b454978..0000000 --- a/docs/src/syntax/extension.md +++ /dev/null @@ -1,87 +0,0 @@ - -MLStyle Extension List -============================= - - - -GADT --------------------- - -- Description: Introduce generic(and implicit) type variables in pattern matching when destructuring data types. -- Conflicts: nothing - -- Example: - -```julia -@use GADT - -@data S{G} begin - S1{T} :: T => S{G} where G -end - -let x :: S{String} = S1(2) - @match x begin - S1{T}(a) where T <: Number => show(a + 1) - _ => show("failed") - end -end -``` -outputs -``` -3 -``` - -UppercaseCapturing ------------------------------ - - -- Description: By default, uppercase symbols cannot be used as patterns for its ambiguous semantics. If you prefer capturing via uppercase symbols, use `UppercaseCapturing`. - -- Conflicts: `Enum` - -- Example: - -```julia -@use UppercaseCapturing - -@match 1 begin - A => A + 1 -end -``` -outputs: -``` -2 -``` - -Enum ------------------------------ - - -- Description: By default, uppercase symbols cannot be used as patterns for its ambiguous semantics. If you prefer replacing patterns like `S()` with `S`, use `Enum`. - - -- Conflicts: `UppercaseCapturing` -- Example: - -```julia -@use Enum -@data A begin - A1() - A2() -end - -@match A1() begin - A1 => 1 - _ => 2 -end -# output: 1 - -@active IsEven(x) begin - x % 2 === 0 -end -@match 4 begin - IsEven => :ok - _ => :err -end -# output: :ok -``` \ No newline at end of file diff --git a/docs/src/syntax/pattern-function.md b/docs/src/syntax/pattern-function.md index 9eccb2d..5b5ce67 100644 --- a/docs/src/syntax/pattern-function.md +++ b/docs/src/syntax/pattern-function.md @@ -6,12 +6,12 @@ Pattern function ```julia f = @λ begin # patterns here - x -> 1 + x => 1 ((x, (1, 2)) && - if x > 3 end) -> 5 - (x, y) -> 2 - ::String -> "is string" - _ -> "is any" + if x > 3 end) => 5 + (x, y) => 2 + ::String => "is string" + _ => "is any" end f(1) # => 1 f((4, (1, 2))) # => 5 @@ -24,6 +24,7 @@ argument in one means: ```julia map((@λ [a, b, c...] -> c), [[1, 2, 3, 4], [1, 2]]) +# Or: map((@λ [a, b, c...] => c), [[1, 2, 3, 4], [1, 2]]) # => 2-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}: # [3, 4] # [] @@ -32,12 +33,12 @@ map((@λ [a, b, c...] -> c), [[1, 2, 3, 4], [1, 2]]) Functionally, A `pattern function` is no more than using a `@match` inside some anonymous function. ```julia - function (x) @match x begin pat1 => body1 pat2 => body2 end end +``` -``` \ No newline at end of file +Both `->` and `=>` works for pattern functions. \ No newline at end of file diff --git a/docs/src/syntax/pattern.md b/docs/src/syntax/pattern.md index 58c5a53..4fb797b 100644 --- a/docs/src/syntax/pattern.md +++ b/docs/src/syntax/pattern.md @@ -12,8 +12,10 @@ Pattern - [Custom Pattern, Dict, Tuple, Array](#Custom-Pattern-1) - [Or Pattern](#Or-Pattern-1) - [ADT destructuring, GADTs](#ADT-Destructuring-1) +- [Records](#Records-1) - [Advanced Type Pattern](#Advanced-Type-Pattern-1) - [Side Effect](#Side-Effect-1) +- [Let Pattern](#Let-Pattern-1) - [Active Pattern](#Active-Pattern-1) - [Expr Pattern](#Expr-Pattern-1) - [Ast Pattern](#Ast-Pattern-1) @@ -102,30 +104,28 @@ Guard end ``` + Predicate --------------- The following has the same semantics as the above snippet. ```julia - function pred(x) x > 5 end @match x begin - x && function pred end => 5 - x # only succeed when x > 5 + x && GuardBy(pred) => 5 - x # only succeed when x > 5 _ => 1 end @match x begin - x && function (x) x > 5 end => 5 - x # only succeed when x > 5 + x && GuardBy(x -> x > 5) => 5 - x # only succeed when x > 5 _ => 1 end - ``` - Range Pattern -------------------- ```julia @@ -153,13 +153,7 @@ end Custom Pattern -------------- -Not recommend to do this for it's implementation specific. -If you want to make your own extensions, check [Pervasives.jl](https://github.com/thautwarm/MLStyle.jl/blob/master/src/Pervasives.jl). - -Defining your own patterns using the low level APIs is quite easy, -but exposing the implementations would cause compatibilities in future development. - - +TODO. Dict, Tuple, Array --------------------- @@ -178,7 +172,6 @@ end - Tuple pattern ```julia - @match (1, 2, (3, 4, (5, ))) begin (a, b, (c, d, (5, ))) => (a, b, c, d) @@ -230,35 +223,25 @@ Tips: `Or Pattern`s could nested. ADT Destructuring --------------- -You can match `ADT` in following 3 means: - -```julia - -C(a, b, c) => ... # ordered arguments -C(b = b) => ... # record syntax -C(_) => ... # wildcard for destructuring - -``` - -Here is an example: +Here is an example, check more about ADTs(and GADTs) at [Algebraic Data Type Syntax in MLStyle](https://thautwarm.github.io/MLStyle.jl/latest/syntax/adt). ```julia @data Example begin Natural(dimension :: Float32, climate :: String, altitude :: Int32) - Cutural(region :: String, kind :: String, country :: String, nature :: Natural) + Cultural(region :: String, kind :: String, country :: String, nature :: Natural) end -神农架 = Cutural("湖北", "林区", "中国", Natural(31.744, "北亚热带季风气候", 3106)) -Yellostone = Cutural("Yellowstone National Park", "Natural", "United States", Natural(44.36, "subarctic", 2357)) +神农架 = Cultural("湖北", "林区", "中国", Natural(31.744, "北亚热带季风气候", 3106)) +Yellostone = Cultural("Yellowstone National Park", "Natural", "United States", Natural(44.36, "subarctic", 2357)) -function my_data_query(data_lst :: Vector{Cutural}) +function my_data_query(data_lst :: Vector{Cultural}) filter(data_lst) do data @match data begin - Cutural(_, "林区", "中国", Natural(dim=dim, altitude)) && + Cultural(_, "林区", "中国", Natural(dim=dim, altitude)) && if dim > 30.0 && altitude > 1000 end => true - Cutural(_, _, "United States", Natural(altitude=altitude)) && + Cultural(_, _, "United States", Natural(altitude=altitude)) && if altitude > 2000 end => true _ => false @@ -269,7 +252,8 @@ my_data_query([神农架, Yellostone]) ... ``` -- Support destructuring Julia types defined regularly +Records +---------------------- ```julia struct A @@ -277,66 +261,80 @@ struct A b c end +@as_record A -# allow `A` to be destructured as datatypes in current module. -@as_record internal A +# or just wrap the struct definition with @as_record +# @as_record struct A +# a +# b +# c +# end @match A(1, 2, 3) begin A(1, 2, 3) => ... end -``` - -- About GADTs -```julia -@use GADT +@match A(1, 2, 3) begin + A(_) => true +end # always true -@data internal Example{T} begin - A{T} :: (Int, T) => Example{Tuple{Int, T}} -end +@match A(1, 2, 3) begin + A() => true +end # always true -@match A(1, 2) begin - A{T}(a :: Int, b :: T) where T <: Number => (a == 1 && T == Int) -end +# field punnings(superior than extracting fields) +@match A(1, 2, 3) begin + A(;a, b=b) => a + b +end # 3 +# extract fields +@match A(1, 2, 3) begin + A(a=a, b=b) => a + b +end # 3 ``` Advanced Type Pattern ------------------------- -Instead of `TypeLevel` feature used in v0.1, an ideal type-stable way to destruct types now is introduced here. +We can introduce type parameters via `where` syntax. ```julia @match 1 begin - ::String => String - ::Int => Int -end -# => Int64 + a :: T where T => T +end # => T +``` -@match 1 begin - ::T where T <: AbstractArray => 0 - ::T where T <: Number => 1 -end +However, whenever you're using `where`, DO NOT use locally captured type arguments in the right side of `::`, when `::` is directly under a `where`. -# => 0 -struct S{A, B} - a :: A - b :: B -end +**Wrong use**: -@match S(1, "2") begin - ::S{A} where A => A +```julia +@match (1, (2, 3)) begin + (::T1 where T1, ::Tuple{T1, T2} where T2) => (T1, T2) end -# => Int64 +# T1 not defined +``` -@match S(1, "2") begin - ::S{A, B} where {A, B <: AbstractString} => (A, B) -end -# => (Int64, String) +Workaround 1: +```julia +@match (1, (2, 3)) begin + (::T1 where T1, ::Tuple{T1′, T2} where {T1′, T2}) && + if T1′ == T1 end => (T1, T2) +end +# (Int64, Int64) ``` +Workaround 2: + +```julia +@match (1, (2, 3)) begin + (::T1, (::T1, ::T2)) :: Tuple{T1, Tuple{T1, T2}} where {T1, T2} => + (T1, T2) +end +# (Int64, Int64) +``` Side-Effect ----------------------- @@ -367,42 +365,97 @@ end # 9 They may be not used very often but quite convenient for some specific domain. +**P.S 1**: when assigning variables with `Do`, don't do `Do((x, y) = expr)`, use this: `Do(x = expr[1], y = expr[2])`. Our pattern compile needs to aware the scope change! + +**P.S 2**: `Do[x...]` is an eye candy for `Do(x)`, and so does `Many[x]` for `Many(x)`. **HOWEVER**, do not use `begin end` syntax in `Do[...]` or `Many[...]`. Julia restricts the parser and it'll not get treated as a `begin end` block. + +**P.S 3**: The [`let` pattern](#Let-Pattern-1) is different from the `Do` pattern. + +- `Do[x=y]` changes `x`, but `let x = y end` shadows `x`. `let` may also change a variable's value. Check the documents of `@switch` macro. + +- You can write non-binding in `Do`: `Do[println(1)]`, but you cannot do this in `let` patterns. + + +Let Pattern +------------------- + +```julia +@match 1 begin + let x = 1 end => x +end +``` + +Bind a variable without changing the value of existing variables, i.e., `let` patterns shadow symbols. + +`let` may also change a variable's value. Check the documents of `@switch` macro. + Active Pattern ------------------ This implementation is a subset of [F# Active Patterns](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns). -There're 2 distinct active patterns, first of which is the normal form: +There're 3 distinct active patterns, first of which is the normal form: ```julia +# 1-ary deconstruction: return Union{Some{T}, Nothing} @active LessThan0(x) begin - if x > 0 + if x >= 0 nothing else - x + Some(x) end end @match 15 begin - LessThan0(_) => :a - _ => :b -end # :b + LessThan0(a) => a + _ => 0 +end # 0 @match -15 begin LessThan0(a) => a _ => 0 end # -15 +# 0-ary deconstruction: return Bool +@active IsLessThan0(x) begin + x < 0 +end + +@match 10 begin + IsLessThan0() => :a + _ => :b +end # b + +# (n+2)-ary deconstruction: return Tuple{E1, E2, ...} +@active SplitVecAt2(x) begin + (x[1:2], x[2+1:end]) +end + +@match [1, 2, 3, 4, 7] begin + SplitVecAt2(a, b) => (a, b) +end +# ([1, 2], [3, 4, 7]) + ``` -The second is the parametric version. +Above 3 cases can be enhanced by becoming **parametric**: ```julia + +@active SplitVecAt{N::Int}(x) begin + (x[1:N], x[N+1:end]) +end + +@match [1, 2, 3, 4, 7] begin + SplitVecAt{2}(a, b) => (a, b) +end +# ([1, 2], [3, 4, 7]) + @active Re{r :: Regex}(x) begin res = match(r, x) if res !== nothing # use explicit `if-else` to emphasize the return should be Union{T, Nothing}. - res + Some(res) else nothing end @@ -413,46 +466,23 @@ end _ => @error "" end # RegexMatch("123") +``` + +Sometimes the enum syntax is useful and convenient: +```julia @active IsEven(x) begin - if x % 2 === 0 - # use explicit `if-else` to emphasize the return should be true/false. - true - else - false - end + x % 2 === 0 end -@match 4 begin - IsEven() => :even - _ => :odd -end # :even - -@match 3 begin - IsEven() => :even - _ => :odd -end # :odd -``` - -Note that the pattern `A{a, b, c}` is equivalent to `A{a, b, c}()`. - -When enabling the extension `Enum` with `@use Enum`, the pattern `A` is equivalent to `A()`: +MLStyle.is_enum(::Type{IsEven}) = true -```julia -@use Enum -@match 4 begin +@match 6 begin IsEven => :even _ => :odd end # :even - -@match 3 begin - IsEven => :even - _ => :odd -end # :odd ``` -Finally, you can customize the visibility of your own active patterns by giving it a qualifier. - Expr Pattern ------------------- @@ -474,19 +504,17 @@ function extract_name(e) " $e\n") end end -@assert extract_name(:(quote +@assert :f == extract_name(:( function f() 1 + 1 end -end)) == :f +)) ``` Ast Pattern -------------------------- -This might be the most important update since v0.2. - ```julia rmlines = @λ begin e :: Expr -> Expr(e.head, filter(x -> x !== nothing, map(rmlines, e.args))...) @@ -553,7 +581,7 @@ end $(block2...) end - end && if (isempty(block1) && isempty(block2)) end => + end && if isempty(block1 && isempty(block2) end => Dict(:funcname => funcname, :firstarg => firstarg, @@ -563,8 +591,16 @@ end :app_fn => app_fn, :app_arg => app_arg) end -``` +# Dict{Symbol,Any} with 7 entries: +# :app_fn => :e +# :args => Any[:b, :c] +# :firstarg => :a +# :funcname => :f +# :other_bindings => Any[:(e = (x->begin… +# :last_operand => :c +# :app_arg => :d +``` Here is several articles about Ast Patterns. diff --git a/docs/src/syntax/qualifier.md b/docs/src/syntax/qualifier.md deleted file mode 100644 index e3de2f3..0000000 --- a/docs/src/syntax/qualifier.md +++ /dev/null @@ -1,89 +0,0 @@ -Pattern Scoping Qualifier -================================== - -To avoid scoping pollution, we introduce a mechanism to allow customizing a pattern's visibility. - -This is supported in the definitions of ADTs/GADTs and active patterns. - -Public ------------------------------- - -Unless specified otherwise, all patterns are defined with a `public` qualifier. - -```julia -@data A begin - ... -end -@active B(x) begin - ... -end -``` -Above snippet is equivalent to -```julia -@data public A begin - ... -end -@active public B(x) begin - ... -end -``` - -`public` means that the pattern is visible once it's imported into current scope. - -Internal --------------------------------------- - -`internal` means that a pattern is only visible in the module it's defined in. In this situation, even if you export the pattern to other modules, it just won't work. - -```julia -module A -using MLStyle -export Data, Data1 -@data internal Data begin - Data1(Int) -end - -@match Data1(2) begin - Data1(x) => @info x -end - -module A2 - using ..A - using MLStyle - @match Data1(2) begin - Data1(x) => @info x - end -end -end -``` - -outputs: - -``` -[ Info: 2 -ERROR: LoadError: MLStyle.Err.PatternUnsolvedException("invalid usage or unknown application case Main.A.Data1(Any[:x]).") -``` - -When it comes to active patterns, the behavior is the same. - -Visible-In ----------------------- - -Sometimes users need to have a more fine-grained control over the patterns' visibility, thus we have provided such a way to allow patterns to be visible in several modules specified by one's own. - -```julia -@active visible in (@__MODULE__) IsEven(x) begin - x % 2 === 4 -end -``` - -Above `IsEven` is only visible in current module. - - -```julia -@active visible in [MyPack.A, MyPack.B] IsEven(x) begin - x % 2 === 4 -end -``` - -Above `IsEven` is only visible in modules `MyPack.A` and `MyPack.B`. \ No newline at end of file diff --git a/docs/src/syntax/switch.md b/docs/src/syntax/switch.md new file mode 100644 index 0000000..2714575 --- /dev/null +++ b/docs/src/syntax/switch.md @@ -0,0 +1,40 @@ + +The Switch Statement +=============================== + +In the real use of pattern matching provided by MLStyle, +things can sometimes becomes painful. To end this, we have `@when`, and now even **`@switch`**. + +Following code is quite stupid in many aspects: + +```julia +var = 1 +@match x begin + (var_, _) => begin + var = var_ + # do stuffs + end +``` + +Firstly, capturing in `@match` just shadows outer variables, but sometimes you just want to change them. + +Secondly, `@match` is an expression, and the right side of `=>` can be only an expression, and writing a `begin end` there can bring bad code format. + +To address these issues, we present the `@switch` macro: + +```julia +var = 1 +x = (33, 44) +@switch x begin +@case (var, _) + println(var) +end +# print: 33 +var # => 33 + +@switch 1 begin +@case (var, _) + println(var) +end +# ERROR: matching non-exhaustive, at #= REPL[n]:1 =# +``` \ No newline at end of file diff --git a/docs/src/tutorials/capture.md b/docs/src/tutorials/capture.md index 1e04f2a..8a22d4d 100644 --- a/docs/src/tutorials/capture.md +++ b/docs/src/tutorials/capture.md @@ -14,7 +14,22 @@ As the motivation of some contributors, `@capture` of `MacroTools.jl` has 3 foll We can implement several new `@capture` via MLStyle.jl to get better in all aspects. +`Capture` Pattern from `MLStyle.Modules.AST`: +===================================================== +MLStyle now can collaborate with scope information very well. You can get the captured(by pattern matching) variables in one point of your program. + +```julia +using MLStyle.Modules.AST +println(Capture) # => Capture +@match :(a + b + c) begin + :($a + $b + $c) && Capture(scope) => scope +end +# Dict{Symbol,Symbol} with 3 entries: +# :a => :a +# :b => :b +# :c => :c +``` RAII-Style ===================== @@ -53,37 +68,4 @@ node2 = :(f(x)) end # do nothing -``` - - -Regex-Style -================== - -This implementation collects captured variables into a dictionary, just like groups in regex but more powerful. - -For we have to analyse which variables to be caught, this implementation could be a bit verbose(100 lines about scoping analysis) and might not work with your own patterns(application patterns/recognizers and active-patterns are okay). - - -Check [MLStyle-Playground](https://github.com/thautwarm/MLStyle-Playground/blob/master/StaticallyCapturing.jl) for implementation codes. - -```julia -@info @capture f($x) :(f(1)) -# Dict(:x=>1) - -destruct_fn = @capture function $(fname :: Symbol)(a, $(args...)) $(body...) end - -@info destruct_fn(:( - function f(a, x, y, z) - x + y + z - end -)) - -# Dict{Symbol,Any}( -# :args => Any[:x, :y, :z], -# :body=> Any[:(#= StaticallyCapturing.jl:93 =#), :(x + y + z)], -# :fname=>:f -# ) -``` - - - +``` \ No newline at end of file diff --git a/docs/src/tutorials/query-lang.md b/docs/src/tutorials/query-lang.md index 4512f1a..5aa3e5b 100644 --- a/docs/src/tutorials/query-lang.md +++ b/docs/src/tutorials/query-lang.md @@ -1,6 +1,8 @@ Write You A Query Language =============================================== +**P.S**: *this document hasn't got up-to-date*. + You may have heard of LINQ or extension methods before, and they're all embedded query languages. In terms of Julia ecosystem, there're already Query.jl, LightQuery.jl, DataFramesMeta.jl, etc., each of which reaches the partial or full features of a query language. diff --git a/src/AbstractPatterns/ADT.jl b/src/AbstractPatterns/ADT.jl new file mode 100644 index 0000000..86a5526 --- /dev/null +++ b/src/AbstractPatterns/ADT.jl @@ -0,0 +1,76 @@ +@nospecialize +export TagfulPattern, And, Or, + Literal, Wildcard, + Deconstrucution, Guard, Effect, + untagless, TagfulPattern, + PatternInfo + +abstract type TagfulPattern end + + +struct PatternInfo + pattern ::TagfulPattern + typetag :: TypeObject +end + +struct And <: TagfulPattern + ps :: Vector{PatternInfo} +end + +struct Or <: TagfulPattern + ps :: Vector{PatternInfo} +end + +struct Literal{T} <: TagfulPattern + val :: T +end + +struct Wildcard <: TagfulPattern +end + +struct Deconstrucution <: TagfulPattern + comp :: PComp + extract :: Function + params :: Vector{PatternInfo} +end + +struct Guard <: TagfulPattern + predicate :: Any +end + +struct Effect <: TagfulPattern + perform :: Any +end + +@specialize +function _uncurry_call_argtail(f) + function (_, args...) + f(args...) + end +end +@nospecialize + +function untagless(points_of_view::Dict{Function, Int}) + myviewpoint = points_of_view[untagless] + typetag_viewpoint::Int = points_of_view[tag_extract] + mk_info(all_info)::PatternInfo = PatternInfo( + all_info[myviewpoint], all_info[typetag_viewpoint] + ) + ! = mk_info + function decons(comp::PComp, extract::Function, ps) + Deconstrucution(comp, extract, PatternInfo[!p for p in ps]) + end + and(ps::Vector{Vector{Any}}) = And(PatternInfo[!e for e in ps]) + or(ps::Vector{Vector{Any}}) = Or(PatternInfo[!e for e in ps]) + + ( + and = and, + or = or, + literal = Literal, + wildcard = Wildcard(), + decons = decons, + guard = Guard, + effect = Effect + ) +end +@specialize \ No newline at end of file diff --git a/src/AbstractPatterns/AbstractPattern.jl b/src/AbstractPatterns/AbstractPattern.jl new file mode 100644 index 0000000..02da556 --- /dev/null +++ b/src/AbstractPatterns/AbstractPattern.jl @@ -0,0 +1,95 @@ +module AbstractPattern +export spec_gen, runterm, MK, RedyFlavoured, TypeObject +export and, or, literal, and, wildcard, decons, + guard, effect +export PatternCompilationError, Target, PatternImpl, PComp +export APP, NoncachablePre, NoPre +export ChainDict, for_chaindict, child, for_chaindict_dup +export BasicPatterns +export P_bind, P_tuple, P_type_of, P_vector, P_capture, P_vector3, P_slow_view, P_fast_view +export P_svec, P_svec3 +export SimpleCachablePre, see_captured_vars +export CFGSpec, CFGJump, CFGLabel, CFGItem, init_cfg + +mutable struct CFGSpec + exp :: Expr +end + +struct CFGItem + kind :: Symbol + name :: Symbol +end + +CFGJump(x::Symbol) = CFGItem(:symbolicgoto, x) +CFGLabel(x::Symbol) = CFGItem(:symboliclabel, x) + +init_cfg(ex::Expr) = init_cfg(CFGSpec(ex)) +function init_cfg(cfg::CFGSpec) + exp = copy(cfg.exp) + cfg_info = Dict{Symbol, Symbol}() + init_cfg!(exp, cfg_info) + exp +end + +function init_cfg!(ex::Expr, cf_info::Dict{Symbol, Symbol}) + args = ex.args + for i in eachindex(args) + @inbounds arg = args[i] + if arg isa CFGItem + label = get!(cf_info, arg.name) do + gensym(arg.name) + end + @inbounds args[i] = Expr(arg.kind, label) + elseif arg isa Expr + init_cfg!(arg, cf_info) + elseif arg isa CFGSpec + @inbounds args[i] = init_cfg(arg) + end + end +end + +include("DataStructure.jl") +include("Target.jl") +include("PatternSignature.jl") +include("Print.jl") +include("structures/Print.jl") +include("structures/TypeTagExtraction.jl") +include("ADT.jl") +include("CaseMerge.jl") +include("UserSignature.jl") +include("Retagless.jl") +include("impl/RedyFlavoured.jl") +include("impl/BasicPatterns.jl") +using .BasicPatterns + +@nospecialize +function MK(m::Any) + m.backend +end + +function runterm(term, xs) + points_of_view = Dict{Function, Int}(x => i for (i, x) in enumerate(xs)) + impls = Tuple(x(points_of_view) for x in xs) + term(impls) +end + +function spec_gen(branches :: Vector) + cores = Branch[] + ln = LineNumberNode(1, "") + for branch in branches + branch isa LineNumberNode && begin + ln = branch + continue + end + (branch, cont) = branch :: Pair{F, Symbol} where F <: Function + points_of_view = Dict{Function, Int}(tag_extract => 1, untagless => 2) + impls = (tag_extract(points_of_view), untagless(points_of_view)) + type, pat = branch(impls) + push!(cores, PatternInfo(pat::TagfulPattern, type::TypeObject) => (ln, cont)) + end + split_cores = Branch[] + case_split!(split_cores, cores) + case_merge(split_cores) +end + +end # module diff --git a/src/AbstractPatterns/CaseMerge.jl b/src/AbstractPatterns/CaseMerge.jl new file mode 100644 index 0000000..a611086 --- /dev/null +++ b/src/AbstractPatterns/CaseMerge.jl @@ -0,0 +1,142 @@ +abstract type AbstractCase end +export AbstractCase, EnumCase, SwitchCase, Leaf, Shaped + +Continuation = Symbol +Branch = Pair{PatternInfo,Tuple{LineNumberNode,Continuation}} + +"""for generating patterns with one-by-one checks +""" +struct EnumCase <: AbstractCase + cases::Vector{AbstractCase} +end + +"""for generating patterns with a optimized switch +""" +struct SwitchCase <: AbstractCase + cases::Dict{TypeObject,AbstractCase} +end + +"""specifying the code body of a case +""" +struct Leaf <: AbstractCase + cont::Continuation +end + +"""checking the shape of the datum with the defined patterns +""" +struct Shaped <: AbstractCase + pattern::PatternInfo + ln::LineNumberNode + case::AbstractCase +end + +@nospecialize + +function case_split!(result::Vector{Branch}, branches::Vector{Branch}) + for (p, (ln, branch)) in branches + if p.pattern isa Or + case_split!(result, Branch[info => (ln, branch) for info in p.pattern.ps]) + else + push!(result, p => (ln, branch)) + end + end +end + +function build_dyn(top::TypeObject, branches::Vector{Branch})::AbstractCase + length(branches) === 1 && return begin + br = branches[1] + Shaped(br.first, br.second[1], Leaf(br.second[2])) + end + @assert !isempty(branches) + enum_cases = AbstractCase[] + labels = Vector{TypeObject}(undef, length(branches)) + groups = [1] + for i in eachindex(branches) + current_type = branches[i].first.typetag + @assert current_type <: top + if current_type === top + # Detected forcely specified matching order. + # e.g: + # match val with + # | (_: Int) -> ... + # | _ -> ... + # | (_ : String) -> .. + # Although Int and String are orthogonal, + # the second pattern `_` breaks their merging + push!(groups, i) + push!(groups, i + 1) + labels[i] = top + continue + end + + last_group_start = groups[end] + non_orthogonal_types = TypeObject[] + non_orthogonal_indices = Int[] + for j in last_group_start:i-1 + if typeintersect(current_type, labels[j]) !== Base.Bottom + # Merging branches + # e.g: + # match val with + # | (_ : Int) -> + # | (_ : String) -> + # | (_ : Float64) -> + # We gives the 3 patterns such a label array: + # [Real, String, Real] + # where, the 1st and 3rd branches merged + push!(non_orthogonal_indices, j) + push!(non_orthogonal_types, labels[j]) + end + end + merged_type = reduce(typejoin, non_orthogonal_types, init = current_type) + for j in non_orthogonal_indices + labels[j] = merged_type + end + labels[i] = merged_type + end + + push!(groups, length(branches) + 1) + n_groups = length(groups) + # fix-point check + if n_groups === 2 && all(labels .=== top) + return EnumCase(AbstractCase[ + Shaped(pat, ln, Leaf(cont)) for (pat, (ln, cont)) in branches + ]) + end + for i_group in 1:n_groups-1 + start = groups[i_group] + final = groups[i_group+1] - 1 + + if start === final + br = branches[start] + push!(enum_cases, Shaped(br.first, br.second[1], Leaf(br.second[2]))) + continue + elseif start > final + continue + end + + switch_map = Dict{TypeObject,Vector{Branch}}() + for i in start:final + vec = get!(switch_map, labels[i]) do + Branch[] + end + push!(vec, branches[i]) + end + switch = Pair{TypeObject,AbstractCase}[ + top′ => build_dyn(top′, branches′) for (top′, branches′) in switch_map + ] + push!(enum_cases, SwitchCase(Dict(switch))) + end + + if length(enum_cases) === 1 + enum_cases[1] + else + EnumCase(enum_cases) + end +end + +function case_merge(branches::Vector{Branch}) + top = reduce(typejoin, TypeObject[pattern.typetag for (pattern, _) in branches]) + build_dyn(top, branches) +end + +@specialize diff --git a/src/AbstractPatterns/DataStructure.jl b/src/AbstractPatterns/DataStructure.jl new file mode 100644 index 0000000..83026e2 --- /dev/null +++ b/src/AbstractPatterns/DataStructure.jl @@ -0,0 +1,76 @@ +struct ChainDict{K, V} + cur :: Dict{K, V} + init :: Ref{ChainDict{K, V}} +end + +ChainDict(cur::Dict{K, V}, init::ChainDict{K, V}) where {K, V} = ChainDict(cur, Ref(init)) +ChainDict(cur::Dict{K, V}) where {K, V} = ChainDict(cur, Ref{ChainDict{K, V}}()) +ChainDict{K, V}() where {K, V} = ChainDict(Dict{K, V}()) + +function Base.get!(f::Function, c::ChainDict{K, V}, k::K)::V where {K, V} + get!(c.cur, k) do + f() + end +end + +function Base.get(c::ChainDict{K, V}, k::K)::V where {K, V} + get(c.cur, k) do + isassigned(c.init) ? + get(c.init[], k) : + throw(KeyError(k)) + end +end + +function Base.get(f::Function, c::ChainDict{K, V}, k::K) where {K, V} + get(c.cur, k) do + isassigned(c.init) ? + get(f, c.init[], k) : + f() + end +end + +function for_chaindict(f::Function, d::ChainDict{K, V}) where {K, V} + keys = Set{K}() + while true + for (k, v) in d.cur + if k in keys + continue + else + push!(keys, k) + end + f(k, v) + end + if isassigned(d.init) + d = d.init[] + else + return + end + end +end + +function for_chaindict_dup(f::Function, d::ChainDict{K, V}) where {K, V} + while true + for (k, v) in d.cur + f(k, v) + end + if isassigned(d.init) + d = d.init[] + else + return + end + end +end + +Base.getindex(c::ChainDict, k) = Base.get(c, k) + +function Base.setindex!(c::ChainDict{K, V}, value::V, k::K) where {K, V} + c.cur[k] = value +end + +function child(c::ChainDict{K, V}) where {K, V} + ChainDict(Dict{K, V}(), c) +end + +function parent(c::ChainDict{K, V}) where {K, V} + c.init[] +end \ No newline at end of file diff --git a/src/AbstractPatterns/PatternSignature.jl b/src/AbstractPatterns/PatternSignature.jl new file mode 100644 index 0000000..40306af --- /dev/null +++ b/src/AbstractPatterns/PatternSignature.jl @@ -0,0 +1,121 @@ +using MLStyle.Err: PatternCompilationError + +PatternImpl = NamedTuple{ + (:and, :or, :literal, :wildcard, :decons, :guard, :effect), +} + + +PatternImpls{N} = NTuple{N, PatternImpl} + +@nospecialize +and(args :: Any...) = and(collect(args)) +and(ps::Vector) = function apply(impls::PatternImpls{N}) where N + xs = [p(impls) for p in ps] + me = Vector{Any}(undef, N) + for i in 1:N + @inbounds me[i] = impls[i].and(xs) + end + me +end + +or(args :: Any...) = or(collect(args)) +or(ps::Vector) = function apply(impls::PatternImpls{N}) where N + xs = [p(impls) for p in ps] + me = Vector{Any}(undef, N) + for i in 1:N + me[i] = impls[i].or(xs) + end + me +end + +literal(val) = function apply(impls::PatternImpls{N}) where N + me = Vector{Any}(undef, length(impls)) + for i in 1:N + me[i] = impls[i].literal(val) + end + me +end + +function wildcard(impls::PatternImpls{N}) where N + me = Vector{Any}(undef, length(impls)) + for i in 1:N + me[i] = impls[i].wildcard + end + me +end + +guard(pred) = function apply(impls::PatternImpls{N}) where N + me = Vector{Any}(undef, N) + for i in 1:N + me[i] = impls[i].guard(pred) + end + me +end + +""" +abstract pure process +""" +abstract type APP end + +struct NoncachablePre <: APP + callable :: Any +end +(f::NoncachablePre)(target::Any) = f.callable(target) +struct NoPre <: APP end + +"""composite pattern +""" +struct PComp + repr :: AbstractString + tcons :: Function + guard1 :: APP + view :: APP + guard2 :: APP +end + +invalid_extract(_, _) = error("impossible") + +function PComp( + repr :: AbstractString, + tcons::Function; + guard1::APP=NoPre(), + view::APP=NoPre(), + guard2::APP=NoPre() +) + PComp(repr, tcons, guard1, view, guard2) +end + +const any_type(_...) = Any +const as_is_comp = PComp("identity", any_type) + + +decons(extract, ps) = decons(as_is_comp, extract, ps) +decons(comp::PComp, ps; extract=invalid_extract) = decons(comp, extract, ps) +decons(comp::PComp, extract::Function, ps) = function apply(impls::PatternImpls{N}) where N + xs = [p(impls) for p in ps] + me = Vector{Any}(undef, N) + for i in 1:N + me[i] = impls[i].decons(comp, extract, xs) + end + me +end + +effect(ctx_perf) = function apply(impls::PatternImpls{N}) where N + me = Vector{Any}(undef, N) + for i in 1:N + me[i] = impls[i].effect(ctx_perf) + end + me +end + +@specialize + +const self = ( + and = and, + or = or, + literal = literal, + wildcard = wildcard, + decons = decons, + guard = guard, + effect = effect +) diff --git a/src/AbstractPatterns/Print.jl b/src/AbstractPatterns/Print.jl new file mode 100644 index 0000000..884a7a4 --- /dev/null +++ b/src/AbstractPatterns/Print.jl @@ -0,0 +1,30 @@ +@nospecialize +Print = ( + indent = function Print_indent(p) + function (io::IO, prefix::AbstractString) + prefix = " " * prefix + p(io, prefix) + end + end, + line = function Print_line(io::IO, prefix :: AbstractString) + println(io) + print(io, prefix) + end, + w = function Print_word(s::AbstractString) + function write(io::IO, ::AbstractString) + print(io, s) + end + end, + seq = function Print_seq(ps...) + function (io::IO, prefix::AbstractString) + prefix = prefix + for p in ps + p(io, prefix) + end + end + end, + run = function Print_run(io::IO, builder) + builder(io, "") + end +) +@specialize \ No newline at end of file diff --git a/src/AbstractPatterns/Retagless.jl b/src/AbstractPatterns/Retagless.jl new file mode 100644 index 0000000..454dc51 --- /dev/null +++ b/src/AbstractPatterns/Retagless.jl @@ -0,0 +1,37 @@ +# restore tagless final encoding from Core +export re_tagless + +@nospecialize +function re_tagless(pi :: PatternInfo, ln :: LineNumberNode) + config = (type=pi.typetag, ln=ln) + re_tagless(config, pi.pattern) +end + +function re_tagless(config :: NamedTuple, p :: And) + UserSitgnature.and([re_tagless(e, config.ln) for e in p.ps], config) +end + +function re_tagless(config :: NamedTuple, p::Or) + UserSitgnature.or([re_tagless(e, config.ln) for e in p.ps], config) +end + +function re_tagless(config :: NamedTuple, p :: Literal) + UserSitgnature.literal(p.val, config) +end + +function re_tagless(config :: NamedTuple, p :: Wildcard) + UserSitgnature.wildcard(config) +end + +function re_tagless(config :: NamedTuple, p :: Deconstrucution) + UserSitgnature.decons(p.comp::PComp, p.extract, [re_tagless(p, config.ln) for p in p.params], config) +end + +function re_tagless(config :: NamedTuple, p :: Guard) + UserSitgnature.guard(p.predicate, config) +end + +function re_tagless(config :: NamedTuple, p :: Effect) + UserSitgnature.effect(p.perform, config) +end +@specialize \ No newline at end of file diff --git a/src/AbstractPatterns/Target.jl b/src/AbstractPatterns/Target.jl new file mode 100644 index 0000000..bcfe882 --- /dev/null +++ b/src/AbstractPatterns/Target.jl @@ -0,0 +1,61 @@ +TypeObject = Union{DataType, Union, UnionAll} + +"""representing the in-matching object in pattern compile time +""" +struct Target{IsComplex} + repr::Any + type::Ref{TypeObject} +end + +@nospecialize + +function target_method end + +function Base.getproperty(target::Target, sym::Symbol) + target_method(target, Val(sym)) +end + +function target_method(target::Target, ::Val{:type_narrow!}) + function (ty::TypeObject) + tyref = getfield(target, :type) + if tyref[] <: ty + else + tyref[] = ty + end + end +end + +function target_method(target::Target, ::Val{:type}) + getfield(target, :type)[] +end + + +function target_method(target::Target, ::Val{:repr}) + getfield(target, :repr) +end + +function target_method(target::Target{IsC}, ::Val{:with_repr}) where IsC + function ap(repr::Any) + Target{IsC}(repr, getfield(target, :type)) + end + function ap(repr::Any, ::Val{IsC′}) where IsC′ + Target{IsC′}(repr, getfield(target, :type)) + end + ap +end + +function target_method(target::Target{IsC}, ::Val{:with_type}) where IsC + function ap(ty::TypeObject) + Target{IsC}(target.repr, Ref{TypeObject}(ty)) + end + function ap(ty::TypeObject, ::Val{IsC′}) where IsC′ + Target{IsC′}(target.repr, Ref{TypeObject}(ty)) + end + ap +end + +function target_method(target::Target{IsC}, ::Val{:clone}) where IsC + Target{IsC}(target.repr, Ref{TypeObject}(target.type)) +end + +@specialize \ No newline at end of file diff --git a/src/AbstractPatterns/UserSignature.jl b/src/AbstractPatterns/UserSignature.jl new file mode 100644 index 0000000..88478fa --- /dev/null +++ b/src/AbstractPatterns/UserSignature.jl @@ -0,0 +1,60 @@ +module UserSitgnature +import MLStyle.AbstractPattern: PComp +export and, or, literal, and, wildcard, decons, + guard, effect, self + +PatternUse = NamedTuple{ + ( + :and, + :or, + :literal, + :wildcard, + :decons, + :guard, + :effect + ) +} + +@nospecialize +_empty_ntuple = NamedTuple() +and(ps::Vector, config::NamedTuple=_empty_ntuple) = function apply(impl::PatternUse) + xs = [p(impl) for p in ps] + impl.and(xs, config) +end + +or(ps::Vector, config::NamedTuple=_empty_ntuple) = function apply(impl::PatternUse) + xs = [p(impl) for p in ps] + impl.or(xs, config) +end +literal(val, config::NamedTuple=_empty_ntuple) = function apply(impl::PatternUse) + impl.literal(val, config) +end +wildcard(config::NamedTuple=_empty_ntuple) = function apply(impl::PatternUse) + impl.wildcard(config) +end + +decons(comp::PComp, extract::Function, ps, config::NamedTuple=_empty_ntuple) = function apply(impl::PatternUse) + xs = [p(impl) for p in ps] + impl.decons(comp, extract, xs, config) +end + +guard(pred, config::NamedTuple=_empty_ntuple) = function apply(impl::PatternUse) + impl.guard(pred, config) +end + +effect(ctx_perf, config::NamedTuple=_empty_ntuple) = function apply(impl::PatternUse) + impl.effect(ctx_perf, config) +end + +const self = ( + and = and, + or = or, + literal = literal, + wildcard = wildcard, + decons = decons, + guard = guard, + effect = effect +) + +end +@specialize \ No newline at end of file diff --git a/src/AbstractPatterns/impl/BasicPatterns.jl b/src/AbstractPatterns/impl/BasicPatterns.jl new file mode 100644 index 0000000..0f7e9f3 --- /dev/null +++ b/src/AbstractPatterns/impl/BasicPatterns.jl @@ -0,0 +1,228 @@ +module BasicPatterns +using MLStyle.AbstractPattern +using MLStyle.AbstractPattern.RedyFlavoured + +export P_bind, P_tuple, P_type_of, P_vector, P_capture, P_vector3, P_slow_view, P_fast_view +export P_svec, P_svec3 +export SimpleCachablePre, see_captured_vars +@nospecialize +OptionalLn = Union{LineNumberNode,Nothing} + +function see_captured_vars(inner::Any, in_scope::ChainDict{Symbol, Symbol}) + bind = Expr(:block) + for_chaindict(in_scope) do k, v + push!(bind.args, :($k = $v)) + end + isempty(bind.args) ? inner : Expr(:let, bind, inner) +end + +struct SimpleCachablePre <: APP + f :: Function +end +(f::SimpleCachablePre)(target) = f.f(target) + +function sequence_index(viewed, i::Integer, ::Any, ::Any) + :($viewed[$i]) +end + +function self_index(viewed, i::Integer, ::Any, ::Any) + @assert i === 1 + viewed +end + + +function length_eq_check(seq, n::Int) + if n === 0 + :(isempty($seq)) + else + :(length($seq) === $n) + end +end + + +function mk_type_object(i::Int, ::Type{T}) where {T} + if isabstracttype(T) + TypeVar(Symbol(:var, i), T) + else + T + end +end + +"""match by type +""" +function P_type_of(t, prepr::AbstractString="isa $t") + recog_type() = t + comp = PComp(prepr, recog_type) + decons(comp, []) +end + + +"""bind a symbol +""" +function P_bind(n::Symbol, expr::Any; see_capture=false) + function bind_effect!(target, scope::ChainDict{Symbol,Symbol}, ln::OptionalLn) + expr′ = see_capture ? see_captured_vars(expr, scope) : expr + n′ = scope[n] = gensym(n) + :($n′ = $expr′) + end + effect(bind_effect!) +end + +"""bind a symbol +""" +function P_capture(n::Symbol) + function capture_effect!(target, scope::ChainDict{Symbol,Symbol}, ln::OptionalLn) + if target isa Symbol + scope[n] = target + return nothing + end + n′ = scope[n] = gensym(n) + :($(n′) = $target) + end + effect(capture_effect!) +end + +"""deconstruct a tuple +""" +function P_tuple(fields::AbstractArray, prepr::AbstractString="Tuple") + function type_of_tuple(xs...) + + ts = [mk_type_object(i, xs[i]) for i in eachindex(xs)] + foldl(ts, init=Tuple{ts...}) do last, t + t isa TypeVar ? + UnionAll(t, last) : + last + end + end + comp = PComp(prepr, type_of_tuple) + + decons(comp, sequence_index, fields) +end + +function type_of_vector(types...) + AbstractArray{T, 1} where T + # if length(types) == 0 + # AbstractArray{Any,1} + # else + # Eltype = foldl(typejoin, types) + # AbstractArray{T,1} where {T<:Eltype} + # end +end +"""deconstruct a vector +""" +function P_vector(fields::AbstractArray, prepr::AbstractString="1DVector") + + n_fields = length(fields) + function pred(target) + length_eq_check(target, n_fields) + end + comp = PComp(prepr, type_of_vector; guard1=NoncachablePre(pred)) + decons(comp, sequence_index, fields) +end + + +"""deconstruct a vector +""" +function P_svec(fields::AbstractArray, prepr::AbstractString="svec") + function type_of_svec(_...) + Core.SimpleVector + end + n_fields = length(fields) + function pred(target) + length_eq_check(target, n_fields) + end + comp = PComp(prepr, type_of_svec; guard1=NoncachablePre(pred)) + decons(comp, sequence_index, fields) +end + +"""deconstruct a vector in this way: [a, b, c, pack..., d, e] +""" +function P_vector3(init::AbstractArray, pack::Function, tail::AbstractArray, prepr::AbstractString = "1DVector Pack") + n1 = length(init) + n2 = length(tail) + min_len = length(init) + length(tail) + # function type_of_vector(types...) + # Eltype = foldl( + # typejoin, + # [ + # types[1:n1]..., + # eltype(types[n1+1]), + # types[end-n2:end]... + # ] + # ) + # AbstractArray{T,1} where { + # T<:Eltype + # } + # end + function extract(arr, i::Int, ::Any, ::Any) + if i <= n1 + :($arr[$i]) + elseif i === n1 + 1 + :(view($arr, $n1+1:length($arr)-$n2)) + else + incr = i - n1 - 1 + :($arr[end-$(n2-incr)]) + end + end + function pred(target) + :(length($target) >= $min_len) + end + comp = PComp(prepr, type_of_vector; guard1=NoncachablePre(pred)) + decons(comp, extract, [init; pack; tail]) +end + + +"""deconstruct a vector in this way: [a, b, c, pack..., d, e] +""" +function P_svec3(init::AbstractArray, pack::Function, tail::AbstractArray, prepr::AbstractString = "svec Pack") + n1 = length(init) + n2 = length(tail) + min_len = length(init) + length(tail) + function type_of_svec(types...) + Core.SimpleVector + end + function extract(arr, i::Int, ::Any, ::Any) + if i <= n1 + :($arr[$i]) + elseif i === n1 + 1 + :($arr[$n1+1:end-$n2]) + else + incr = i - n1 - 1 + :($arr[end-$(n2-incr)]) + end + end + function pred(target) + :(length($target) >= $min_len) + end + comp = PComp(prepr, type_of_svec; guard1=NoncachablePre(pred)) + decons(comp, extract, [init; pack; tail]) +end + + +"""untyped view pattern +""" +function P_slow_view(trans, p::Function, prepr::AbstractString="ViewBy($trans)") + function type_of_slow_view(args...) + Any + end + + comp = PComp( + prepr, type_of_slow_view; + view=SimpleCachablePre(trans) + ) + decons(comp, self_index, [p]) +end + +"""typed view pattern +""" +function P_fast_view(tcons, trans, p::Function, prepr::AbstractString="ViewBy($trans, typecons=$tcons)") + + comp = PComp( + prepr, tcons; + view=SimpleCachablePre(trans) + ) + decons(comp, self_index, [p]) +end + +@specialize +end \ No newline at end of file diff --git a/src/AbstractPatterns/impl/RedyFlavoured.jl b/src/AbstractPatterns/impl/RedyFlavoured.jl new file mode 100644 index 0000000..e323441 --- /dev/null +++ b/src/AbstractPatterns/impl/RedyFlavoured.jl @@ -0,0 +1,590 @@ +module RedyFlavoured +using MLStyle.AbstractPattern +using MLStyle.Err: PatternCompilationError + + +Config = NamedTuple{(:type, :ln)} +Scope = ChainDict{Symbol,Symbol} +ViewCache = ChainDict{Pair{TypeObject, Any}, Tuple{Symbol, Bool}} + +function update_parent!(view_cache::ViewCache) + parent = view_cache.init[] + for (typed_viewer, (sym, _)) in view_cache.cur + parent[typed_viewer] = (sym, false) + end +end + +struct CompileEnv + # Dict(user_defined_name => actual_name). mangling for scope safety + scope :: Scope + # Dict(view => (viewed_cache_symbol => guarantee_of_defined?)) + view_cache :: ViewCache + terminal_scope :: Dict{Symbol, Vector{Pair{Dict{Symbol, Symbol}, Expr}}} +end + +abstract type Cond end +struct AndCond <: Cond + left::Cond + right::Cond +end + +struct OrCond <: Cond + left::Cond + right::Cond +end + +struct TrueCond <: Cond + stmt::Any +end + +TrueCond() = TrueCond(true) + +struct CheckCond <: Cond + expr::Any +end + +""" +build to disjunctive forms +""" +function build_readable_expression!( + exprs::Vector{Any}, + following_stmts::Vector{Any}, + cond::CheckCond, +) + expr = cond.expr + if !isempty(following_stmts) + expr = Expr(:block, following_stmts..., expr) + empty!(following_stmts) + end + push!(exprs, expr) +end + +function build_readable_expression!( + exprs::Vector{Any}, + following_stmts::Vector{Any}, + cond::TrueCond, +) + cond.stmt isa Union{Bool,Int,Float64,Nothing} && return #= shall contain more literal typs =# + push!(following_stmts, cond.stmt) + nothing +end + +function build_readable_expression!( + exprs::Vector{Any}, + following_stmts::Vector{Any}, + cond::AndCond, +) + build_readable_expression!(exprs, following_stmts, cond.left) + build_readable_expression!(exprs, following_stmts, cond.right) +end + +function build_readable_expression!( + exprs::Vector{Any}, + following_stmts::Vector{Any}, + cond::OrCond, +) + exprs′ = [] + following_stmts′ = [] + build_readable_expression!(exprs′, following_stmts′, cond.left) + left = to_expression(exprs′, following_stmts′) + + empty!(exprs′) + empty!(following_stmts′) + + build_readable_expression!(exprs′, following_stmts′, cond.right) + right = to_expression(exprs′, following_stmts′) + + empty!(exprs′) + empty!(following_stmts′) + + bool_or = Expr(:||, left, right) + if !isempty(following_stmts) + bool_or = Expr(:block, following_stmts..., bool_or) + empty!(following_stmts) + end + + push!(exprs, bool_or) +end + +function to_expression(cond::Cond) + exprs = [] + following_stmts = [] + build_readable_expression!(exprs, following_stmts, cond) + to_expression(exprs, following_stmts) +end + +function to_expression(exprs::Vector{Any}, following_stmts::Vector) + bool_and(a, b) = Expr(:&&, a, b) + if isempty(following_stmts) + isempty(exprs) && return true + foldr(bool_and, exprs) + else + init = Expr(:block, following_stmts..., true) + foldr(bool_and, exprs, init = init) + end +end + +allsame(xs::Vector) = any(e -> e == xs[1], xs) + + +const CACHE_NO_CACHE = 0 +const CACHE_MAY_CACHE = 1 +const CACHE_CACHED = 2 + + +function static_memo( + f::Function, + view_cache::ViewCache, + op::APP; + ty::TypeObject, + depend::Union{Nothing, APP}=nothing +) + if op isa NoPre + nothing + elseif op isa NoncachablePre + cached_sym = nothing + f(cached_sym, CACHE_NO_CACHE) + else + cache_key = depend === nothing ? op : (depend => op) + cache_key = Pair{TypeObject, Any}(ty, cache_key) + cached = get(view_cache, cache_key) do + nothing + end::Union{Tuple{Symbol, Bool}, Nothing} + if cached === nothing + + cached_sym = gensym("cache") + computed_guarantee = false + else + (cached_sym, computed_guarantee) = cached + end + if !computed_guarantee + f(cached_sym, CACHE_MAY_CACHE) + view_cache[cache_key] = (cached_sym, true) + cached_sym + else + f(cached_sym, CACHE_CACHED) + end + end +end + +function init_cache(view_cache::ViewCache) + block = Expr(:block) + cache_syms = block.args + # TODO: OPT + for_chaindict_dup(view_cache) do _, (view_cache_sym, _) + push!(cache_syms, :($view_cache_sym = nothing)) + end + if isempty(cache_syms) + true + else + block + end +end + +function myimpl() + + function cache(f) + function apply(env::CompileEnv, target::Target{true})::Cond + target′ = target.with_repr(gensym(), Val(false)) + AndCond(TrueCond(:($(target′.repr) = $(target.repr))), f(env, target′)) + end + function apply(env::CompileEnv, target::Target{false})::Cond + f(env, target) + end + apply + end + + wildcard(::Config) = (::CompileEnv, target::Target) -> TrueCond() + + literal(v, config::Config) = + function ap_literal(::CompileEnv, target::Target)::Cond + if v isa Symbol + v = QuoteNode(v) + end + CheckCond(:($(target.repr) == $v)) + end + + function and(ps::Vector{<:Function}, config::Config) + @assert !isempty(ps) + function ap_and_head(env::CompileEnv, target::Target{false})::Cond + hd = ps[1]::Function + tl = view(ps, 2:length(ps)) + init = hd(env, target) + + # the first conjuct must be executed, so the computation can get cached: + # e.g., + # match val with + # | View1 && View2 -> + # and we know `View1` must be cached. + (computed_guarantee′, env′, ret) = + foldl(tl, init=(true, env, init)) do (computed_guarantee, env, last), p + # `TrueCond` means the return expression must be evaluated to `true` + computed_guarantee′ = computed_guarantee && last isa TrueCond + if computed_guarantee′ === false && computed_guarantee === true + view_cache = env.view_cache + view_cache′ = child(view_cache) + view_cache_change = view_cache′.cur + env = CompileEnv(env.scope, view_cache′, env.terminal_scope) + end + computed_guarantee′, env, AndCond(last, p(env, target)) + end + + if computed_guarantee′ === false + update_parent!(env′.view_cache) + end + ret + + end |> cache + end + + + function or(ps::Vector{<:Function}, config::Config) + @assert !isempty(ps) + function ap_or(env::CompileEnv, target::Target{false})::Cond + or_checks = Cond[] + scope = env.scope + view_cache = env.view_cache + scopes = Dict{Symbol,Symbol}[] + n_ps = length(ps) + for p in ps + scope′ = child(scope) + env′ = CompileEnv(scope′, view_cache, env.terminal_scope) + push!(or_checks, p(env′, target.clone)) + push!(scopes, scope′.cur) + end + + + # check the change of scope discrepancies for all branches + intersected_new_names = intersect!(Set(keys(scopes[1])), map(keys, view(scopes, 2:n_ps))...) + for key in intersected_new_names + refresh = gensym(key) + for i in eachindex(or_checks) + check = or_checks[i] + old_name = get(scopes[i], key) do + throw(PatternCompilationError( + config.ln, + "Variables such as $key not bound in some branch!", + )) + end + or_checks[i] = + AndCond(or_checks[i], TrueCond(:($refresh = $old_name))) + end + scope[key] = refresh + + end + foldr(OrCond, or_checks) + + end |> cache + end + + # C(p1, p2, .., pn) + # pattern = (target: code, remainder: code) -> code + function decons( + comp::PComp, + extract::Function, + ps::Vector, + config::Config, + ) + ty = config.type + ln = config.ln + + function ap_decons(env, target::Target{false})::Cond + # type check + chk = if target.type <: ty + TrueCond() + else + target.type_narrow!(ty) + CheckCond(:($(target.repr) isa $ty)) + end + + scope = env.scope + # compute pattern viewing if no guarantee of being computed + view_cache = env.view_cache + + target_sym::Symbol = target.repr + viewed_sym::Any = target_sym + static_memo(view_cache, comp.guard1; ty=ty) do cached_sym, cache_flag + if cache_flag === CACHE_CACHED + chk = AndCond(chk, CheckCond(:($cached_sym.value))) + return + end + + if cache_flag === CACHE_NO_CACHE + guard_expr = comp.guard1(target_sym) + chk = AndCond(chk, CheckCond(guard_expr)) + elseif cache_flag === CACHE_MAY_CACHE + guard_expr = comp.guard1(target_sym) + do_cache = Expr(:if, + :($cached_sym === nothing), + :($cached_sym = Some($guard_expr)) + ) + chk = AndCond( + chk, + AndCond( + TrueCond(do_cache), + CheckCond(:($cached_sym.value)) + ) + ) + else + error("impossible: invalid flag") + end + nothing + end + + static_memo(view_cache, comp.view; ty=ty) do cached_sym, cache_flag + if cache_flag === CACHE_NO_CACHE + viewed_sym = gensym() + viewed_expr = comp.view(target_sym) + chk = AndCond( + TrueCond(:($viewed_sym = $viewed_expr)) + ) + elseif cache_flag === CACHE_CACHED + viewed_sym = :($cached_sym.value) + elseif cache_flag === CACHE_MAY_CACHE + viewed_expr = comp.view(target_sym) + do_cache = Expr(:if, + :($cached_sym === nothing), + :($cached_sym = Some($viewed_expr)) + ) + chk = AndCond( + chk, + TrueCond(do_cache) + ) + viewed_sym = :($cached_sym.value) + else + error("impossible: invalid flag") + end + nothing + end + + static_memo(view_cache, comp.guard2; ty=ty, depend=comp.view) do cached_sym, cache_flag + if cache_flag === CACHE_CACHED + chk = AndCond(chk, CheckCond(:($cached_sym.value))) + return + end + + if cache_flag === CACHE_NO_CACHE + guard_expr = comp.guard2(viewed_sym) + chk = AndCond(chk, CheckCond(guard_expr)) + elseif cache_flag === CACHE_MAY_CACHE + guard_expr = comp.guard2(viewed_sym) + do_cache = Expr(:if, + :($cached_sym === nothing), + :($cached_sym = Some($guard_expr)) + ) + chk = AndCond( + chk, + AndCond( + TrueCond(do_cache), + CheckCond(:($cached_sym.value)) + ) + ) + else + error("impossible: invalid flag") + end + nothing + end + + for i in eachindex(ps) + p = ps[i] :: Function + field_target = Target{true}(extract(viewed_sym, i, scope, ln), Ref{TypeObject}(Any)) + view_cache′ = ViewCache() + env′ = CompileEnv(scope, view_cache′, env.terminal_scope) + elt_chk = p(env′, field_target) + chk = AndCond( + chk, + AndCond( + TrueCond(init_cache(view_cache′)), + elt_chk + ) + ) + end + chk + end |> cache + end + + function guard(pred::Function, config::Config) + function ap_guard(env, target::Target{false})::Cond + expr = pred(target.repr, env.scope, config.ln) + expr === true ? TrueCond() : CheckCond(expr) + end |> cache + end + + function effect(perf::Function, config::Config) + function ap_effect(env, target::Target{false})::Cond + TrueCond(perf(target.repr, env.scope, config.ln)) + end |> cache + end + + ( + and = and, + or = or, + literal = literal, + wildcard = wildcard, + decons = decons, + guard = guard, + effect = effect, + ) +end + +const redy_impl = myimpl() + +function compile_spec!( + env::CompileEnv, + suite::Vector{Any}, + x::Shaped, + target::Target{IsComplex}, +) where {IsComplex} + if IsComplex && !(x.case isa Leaf) + sym = gensym() + push!(suite, :($sym = $(target.repr))) + target = target.with_repr(sym, Val(false)) + end + mkcond = re_tagless(x.pattern, x.ln)(redy_impl) + ln = x.ln + push!(suite, ln) + cond = mkcond(env, target) + conditional_expr = to_expression(cond) + true_clause = Expr(:block) + compile_spec!(env, true_clause.args, x.case, target) + push!( + suite, + conditional_expr === true ? true_clause : Expr(:if, conditional_expr, true_clause), + ) +end + +function compile_spec!( + env::CompileEnv, + suite::Vector{Any}, + x::Leaf, + target::Target, +) + + + scope = Dict{Symbol, Symbol}() + for_chaindict(env.scope) do k, v + scope[k] = v + end + if !isempty(scope) + terminal_scope = env.terminal_scope + move = Expr(:block) + branched_terminal = get!(terminal_scope, x.cont) do + Pair{Dict{Symbol, Symbol}, Expr}[] + end + push!(branched_terminal, scope=>move) + push!(suite, move) + end + + push!(suite, CFGJump(x.cont)) +end + +function compile_spec!( + env::CompileEnv, + suite::Vector{Any}, + x::SwitchCase, + target::Target{IsComplex}, +) where {IsComplex} + if IsComplex + sym = gensym() + push!(suite, :($sym = $(target.repr))) + target = target.with_repr(sym, Val(false)) + else + sym = target.repr + end + + for (ty, case) in x.cases + true_clause = Expr(:block) + # create new `view_cache` as only one case will be executed + view_cache′ = child(env.view_cache) + env′ = CompileEnv(child(env.scope), view_cache′, env.terminal_scope) + compile_spec!(env′, true_clause.args, case, target.with_type(ty)) + update_parent!(view_cache′) + push!(suite, Expr(:if, :($sym isa $ty), true_clause)) + end +end + +function compile_spec!( + env::CompileEnv, + suite::Vector{Any}, + x::EnumCase, + target::Target{IsComplex}, +) where {IsComplex} + if IsComplex + sym = gensym() + push!(suite, :($sym = $(target.repr))) + target = target.with_repr(sym, Val(false)) + end + for case in x.cases + # use old view_cache: + # cases are tried in order, + # hence `view_cache` can inherit from the previous case + env′ = CompileEnv(child(env.scope), env.view_cache, env.terminal_scope) + compile_spec!(env′, suite, case, target.clone) + end +end + +function compile_spec(target::Any, case::AbstractCase, ln::Union{LineNumberNode,Nothing}) + target = Target{true}(target, Ref{TypeObject}(Any)) + + + ret = Expr(:block) + suite = ret.args + + scope = Scope() + view_cache = ViewCache() + terminal_scope = Dict{Symbol, Vector{Pair{Dict{Symbol, Symbol}, Expr}}}() + env = CompileEnv(scope, view_cache, terminal_scope) + + compile_spec!(env, suite, case, target) + pushfirst!(suite, init_cache(view_cache)) + + terminal_scope′ = Dict{Symbol, Dict{Symbol, Symbol}}() + for (br, move_semantics) in terminal_scope + n_merge = length(move_semantics) + if n_merge === 1 + name_map, _ = move_semantics[1] + terminal_scope′[br] = name_map + continue + end + hd_name_map = move_semantics[1].first + ∩ˢ = intersect!( + Set(keys(hd_name_map)), + (keys(name_map) for (name_map, _) in move_semantics)... + )::Set{Symbol} + adjusted_move = Dict{Symbol, Symbol}() + ∩ⱽ = sort!(collect(∩ˢ)) + for γᵢ in ∩ⱽ # γᵢ: the i-th captured user symbol + γᵢ∞′ = move_semantics[1].first[γᵢ] # the finally mangled of γᵢ + adjusted_move[γᵢ] = γᵢ∞′ + for i in 2:n_merge + (name_map, move) = move_semantics[i] + γᵢ′ = name_map[γᵢ] # outdated move record + push!(move.args, :($γᵢ∞′ = $γᵢ′)) + end + end + terminal_scope′[br] = adjusted_move + end + + if ln !== nothing + # TODO: better trace + msg = "matching non-exhaustive, at $ln" + push!(suite, :(error($msg))) + else + push!(suite, :(error("no pattern matched"))) + end + + ret = length(suite) === 1 ? suite[1] : ret + terminal_scope′, ret +end + +"""compile a series of `Term => Symbol`(branches) to a Julia expression +""" +function backend( + expr_to_match::Any, + branches::Vector, + ln::Union{LineNumberNode,Nothing} = nothing, +) + spec = spec_gen(branches) + compile_spec(expr_to_match, spec, ln) +end +end diff --git a/src/AbstractPatterns/structures/Print.jl b/src/AbstractPatterns/structures/Print.jl new file mode 100644 index 0000000..6d011d3 --- /dev/null +++ b/src/AbstractPatterns/structures/Print.jl @@ -0,0 +1,57 @@ +@nospecialize +"""The view point of showing patterns +""" +function pretty(points_of_view::Dict{Function, Int}) + viewpoint = points_of_view[pretty] + + function and(ps) + xs = Any[Print.w("(")] + for p in ps + push!(xs, p[viewpoint]) + push!(xs, Print.w(" && ")) + end + pop!(xs) + if !isempty(ps) + push!(xs, Print.w(")")) + end + Print.seq(xs...) + end + + function or(ps) + xs = Any[Print.w("(")] + for p in ps + push!(xs, p[viewpoint]) + push!(xs, Print.w(" || ")) + end + pop!(xs) + if !isempty(ps) + push!(xs, Print.w(")")) + end + Print.seq(xs...) + end + literal(val) = Print.w(string(val)) + wildcard = Print.w("_") + + function decons(comp::PComp, _, ps) + Print.seq(Print.w(comp.repr), Print.w("("), getindex.(ps, viewpoint)..., Print.w(")")) + end + + function guard(pred) + Print.seq(Print.w("when("), Print.w(repr(pred)), Print.w(")")) + end + + function effect(eff) + Print.seq(Print.w("do("), Print.w(repr(eff)), Print.w(")")) + end + + ( + and = and, + or = or, + literal = literal, + wildcard = wildcard, + decons = decons, + guard = guard, + effect = effect + ) +end +@specialize diff --git a/src/AbstractPatterns/structures/TypeTagExtraction.jl b/src/AbstractPatterns/structures/TypeTagExtraction.jl new file mode 100644 index 0000000..74eac0a --- /dev/null +++ b/src/AbstractPatterns/structures/TypeTagExtraction.jl @@ -0,0 +1,55 @@ +@nospecialize +"""the view point of the type tag for each term +""" +function tag_extract(points_of_view::Dict{Function,Int}) + viewpoint = points_of_view[tag_extract] + + function and(many) + @assert !isempty(many) + ts = getindex.(many, viewpoint) + t = reduce(typeintersect, ts) + if t === Base.Bottom + core_msg = "and patterns require an intersection of $(ts), which seems empty!" + error(core_msg) + end + t + end + + function or(many) + ts = getindex.(many, viewpoint) + Union{ts...} + end + + function literal(val) + typeof(val) + end + + wildcard = Any + + function decons(comp::PComp, _, ns) + targs = getindex.(ns, viewpoint) + try + comp.tcons(targs...) + catch e + join(map(repr, targs), ",") + if e isa MethodError && e.f === comp.tcons + argstr = join(repeat(String["_"], length(targs)), ", ") + error("invalid deconstructor $(comp.repr)($(argstr))") + end + rethrow() + end + end + + guard(_) = Any + effect(_) = Any + ( + and = and, + or = or, + literal = literal, + wildcard = wildcard, + decons = decons, + guard = guard, + effect = effect + ) +end +@specialize diff --git a/src/DataType.jl b/src/DataType.jl index ed9a183..939e9d5 100644 --- a/src/DataType.jl +++ b/src/DataType.jl @@ -1,17 +1,10 @@ module DataType -using MLStyle -using MLStyle.Toolz: isCapitalized, ($), cons, nil -using MLStyle.MatchCore +using MLStyle.MatchImpl using MLStyle.Qualification -using MLStyle.Infras -using MLStyle.Pervasives -using MLStyle.Render: render -using MLStyle.Record: def_record -using MLStyle.TypeVarExtraction +using MLStyle.Record: as_record +using MLStyle.ExprTools export @data -is_symbol_capitalized = isCapitalized ∘ string - """ Codegen for `@data`, e.g., @@ -58,150 +51,136 @@ end # => true ``` """ macro data(typ, def_variants) - data(typ, def_variants, :public, __module__) |> esc + esc(data(typ, def_variants, __source__, __module__)) end macro data(qualifier, typ, def_variants) - data(typ, def_variants, qualifier, __module__) |> esc -end - -function get_tvars(t :: UnionAll) - cons(t.var.name, get_tvars(t.body)) + deprecate_qualifiers(string(qualifier)) + esc(data(typ, def_variants, __source__, __module__)) end -function get_tvars(t :: Base.DataType) - nil() -end - - -function get_tvars(t :: Union) - nil() -end - -function data(typ, def_variants, qualifier_ast, mod) - typename = - @match typ begin - :($typename{$(a...)}) => typename - :($typename{$(a...)} <: $b) => typename - :($typename <: $b) => typename - :($(typename :: Symbol)) => typename - end - - mod.eval(:(abstract type $typ end)) - - original_ty = getfield(mod, typename) - tvars_of_abst = get_tvars(original_ty) - - for (ctor_name, pairs, each) in impl(original_ty, def_variants, mod) - mod.eval(each) - ctor = getfield(mod, ctor_name) - qualifier = get_qualifier(qualifier_ast, mod) - def_record(ctor, convert(Vector{Symbol}, map(first, pairs)), qualifier, mod) +function data(typ::Any, def_variants::Expr, line::LineNumberNode, mod::Module) + typename, tvars_of_abst = @match typ begin + :($typename{$(a...)}) => (typename, get_type_parameters_ordered(a)) + :($typename{$(a...)} <: $b) => (typename, get_type_parameters_ordered(a)) + :($typename <: $b) => (typename, Symbol[]) + :($(typename::Symbol)) => (typename, Symbol[]) end - nothing -end - -function impl(t, variants :: Expr, mod :: Module) - l :: LineNumberNode = LineNumberNode(1) - abs_tvars = collect(get_tvars(t)) - defs = [] - abst() = isempty(abs_tvars) ? t : :($t{$(abs_tvars...)}) - VAR = mangle(mod) + ret = Expr(:block, :(abstract type $typ end)) + impl!(ret.args, typename, tvars_of_abst, def_variants, line, mod) + ret +end +function impl!( + suite::AbstractArray, + abs_t, + abs_tvars::Vector{Symbol}, + variants::Expr, + ln::LineNumberNode, + mod::Module +) + abst() = isempty(abs_tvars) ? abs_t : :($abs_t{$(abs_tvars...)}) for each in variants.args - @match each begin - ::LineNumberNode => (l = each) - :($case{$(generic_tvars...)} :: ($(params...), ) => $(ret_ty) where {$(implicit_tvars...)}) || - :($case{$(generic_tvars...)} :: ($(params...), ) => $(ret_ty && Do(implicit_tvars=[]))) || - :($case{$(generic_tvars...)} :: $(arg_ty && Do(params = [arg_ty])) => $ret_ty where {$(implicit_tvars...)}) || - :($case{$(generic_tvars...)} :: $(arg_ty && Do(params = [arg_ty])) => $(ret_ty && Do(implicit_tvars=[]))) || - :($(case && Do(generic_tvars = [])) :: ($(params...), ) => $(ret_ty) where {$(implicit_tvars...)}) || - :($(case && Do(generic_tvars = [])) :: ($(params...), ) => $(ret_ty && Do(implicit_tvars=[]))) || - :($(case && Do(generic_tvars = [])) :: $(arg_ty && Do(params = [arg_ty])) => $(ret_ty) where {$(implicit_tvars...)}) || - :($(case && Do(generic_tvars = [])) :: $(arg_ty && Do(params = [arg_ty])) => $(ret_ty && Do(implicit_tvars=[]))) || - - :($case($((params && Do(generic_tvars=abs_tvars))...))) && Do(ret_ty = abst(), implicit_tvars=[]) => begin - - config = Dict{Symbol, Any}([(gtvar isa Expr ? gtvar.args[1] : gtvar) => Any for gtvar in implicit_tvars]) - - pairs = map(enumerate(params)) do (i, each) - @match each begin - :($(a::Symbol && function (x) !is_symbol_capitalized(x) end)) => (a, Any, Any) - :($(a && function is_symbol_capitalized end)) => (Symbol("_$i"), a, render(a, config)) - :($field :: $ty) => (field, ty, render(ty, config)) - end - end - - definition_body = [Expr(:(::), field, ty) for (field, ty, _) in pairs] - constructor_args = [Expr(:(::), field, ty) for (field, _, ty) in pairs] - arg_names = [field for (field, _, _) in pairs] - getfields = [:($VAR.$field) for field in arg_names] - definition_head = :($case{$(generic_tvars...), $(implicit_tvars...)}) - - generic_tvars = collect(map(to_tvar, extract_tvars(generic_tvars))) - implicit_tvars = collect(map(to_tvar, extract_tvars(implicit_tvars))) - - convert_fn = isempty(implicit_tvars) ? nothing : let (=>) = (a, b) -> convert(b, a) - out_tvars = [generic_tvars; implicit_tvars] - inp_tvars = [generic_tvars; [mangle(mod) for _ in implicit_tvars]] - fresh_tvars1 = [] - fresh_tvars2 = [] - n_generic_tvars = length(generic_tvars) - for i in 1 + n_generic_tvars : length(implicit_tvars) + n_generic_tvars - TAny = inp_tvars[i] - TCov = out_tvars[i] - push!(fresh_tvars2, :($TCov <: $TAny)) - push!(fresh_tvars1, TAny) - end - arg2 = :($VAR :: $case{$(inp_tvars...)}) - arg1_cov = :($Type{$case{$(out_tvars...)}}) - arg1_abs = :($Type{$ret_ty}) - casted = :($case{$(out_tvars...)}($(getfields...))) - quote - $Base.convert(::$arg1_cov, $arg2) where {$(generic_tvars...), $(fresh_tvars1...), $(fresh_tvars2...)} = $casted - $Base.convert(::$arg1_abs, $arg2) where {$(generic_tvars...), $(fresh_tvars1...), $(fresh_tvars2...)} = $casted - end - end - - def_cons = - isempty(generic_tvars) && isempty(implicit_tvars) ? - !isempty(constructor_args) ? - quote - function $case(;$(constructor_args...)) - $case($(arg_names...)) - end - end : - nothing : - let spec_tvars = [generic_tvars; [Any for _ in implicit_tvars]] - if isempty(generic_tvars) - quote - function $case($(constructor_args...), ) - $case{$(spec_tvars...)}($(arg_names...)) - end - end - else - quote - function $case($(constructor_args...), ) where {$(generic_tvars...)} - $case{$(spec_tvars...)}($(arg_names...)) - end + @switch each begin + @case ::LineNumberNode + ln = each + continue + + @case :($case{$(generic_tvars...)}::($(params...),) => + $(ret_ty) where {$(constraints...)} + ) || + :($case{$(generic_tvars...)}::($(params...),) => $ret_ty) && + let constraints = [] + end || + :( + $case{$(generic_tvars...)}::$arg_ty => + $ret_ty where {$(constraints...)} + ) && let params = [arg_ty] + end || + :($case{$(generic_tvars...)}::$arg_ty => $ret_ty) && + let params = [arg_ty], constraints = [] + end || + :($case::($(params...),) => $(ret_ty) where {$(constraints...)}) && + let generic_tvars = [] + end || + :($case::($(params...),) => $ret_ty) && + let generic_tvars = [], constraints = [] + end || + :($case::$arg_ty => $ret_ty where {$(constraints...)}) && + let generic_tvars = [], params = [arg_ty] + end || + :($case::$arg_ty => $ret_ty) && + let generic_tvars = [], params = [arg_ty], constraints = [] + end || + :($case($(params...))) && + let ret_ty = abst(), constraints = [], generic_tvars = abs_tvars + end + + + expr_field_def = Expr(:block, ln) + suite_field_def = expr_field_def.args + field_symbols = Symbol[] + for i in eachindex(params) + each = params[i] + @match each begin + a::Symbol && + # TODO: check length of symbol? + (if Base.isuppercase(string(each)[1]) end && + let field = Symbol("_$i"), ty = a end + || let field = a, ty = Any end) || + :($field :: $ty) || + (:(::$ty) || ty) && let field = Symbol("_$i") end => + begin + argdecl = :($field :: $ty) + push!(suite_field_def, argdecl) + push!(field_symbols, field) + nothing end - end + _ => error("invalid datatype field $each") end - - definition = @format [case, l, ret_ty, definition_head] quote - struct definition_head <: ret_ty - l - $(definition_body...) - end - $def_cons - $convert_fn - end - push!(defs, (case, pairs, definition)) - end + end + + expr_struct_def = Expr( + :struct, + false, + Expr(:(<:), + Expr(:curly, case, generic_tvars...), + ret_ty + ), + expr_field_def + ) + + expr_infer_constructor = if isempty(constraints) + nothing + else + call = Expr(:call, case, suite_field_def[2:end]...) + type_arguments = get_type_parameters_ordered(generic_tvars) + fn_sig = Expr(:where, call, generic_tvars...) + Expr( + :function, + fn_sig, + Expr(:block, + Expr(:let, + Expr(:block, constraints...), + Expr(:call, + :($case{$(type_arguments...)}), + field_symbols... + ) + ) + ) + ) + end + + expr_setup_record = as_record(case, ln, mod) + push!( + suite, + expr_struct_def, + expr_infer_constructor, + expr_setup_record + ) + # TODO: @case _ end end - defs end - end # module diff --git a/src/Err.jl b/src/Err.jl index 13854cb..0ba1424 100644 --- a/src/Err.jl +++ b/src/Err.jl @@ -1,14 +1,11 @@ module Err -export PatternUnsolvedException, InternalException, SyntaxError, UnknownExtension, @syntax_err +export PatternCompilationError, InternalException, SyntaxError, UnknownExtension, PatternUnsolvedException -struct PatternUnsolvedException <: Exception - msg :: String - PatternUnsolvedException(arg) = - if isa(arg, String) - new(arg) - else - new("Non-exhaustive pattern found for target `$(repr(arg))`.") - end +PatternUnsolvedException = ErrorException + +struct PatternCompilationError <: Exception + line::Union{LineNumberNode,Nothing} + msg::AbstractString end struct InternalException <: Exception @@ -23,8 +20,4 @@ struct UnknownExtension <: Exception ext :: Union{String, Symbol} end -macro syntax_err(msg) - esc(:($throw($SyntaxError($msg)))) -end - end diff --git a/src/ExprTools.jl b/src/ExprTools.jl new file mode 100644 index 0000000..2728693 --- /dev/null +++ b/src/ExprTools.jl @@ -0,0 +1,47 @@ +module ExprTools +using MLStyle.MatchCore +export take_type_parameters!, get_type_parameters, get_type_parameters_ordered +export @reexport + +function take_type_parameters!(syms, ex)::Nothing + @sswitch ex begin + @case :($a >: $_) || :($a <: $_) + @assert a isa Symbol + push!(syms, a) + return + @case :($_ >: $b >: $_) || :($_ <: $b <: $_) + @assert b isa Symbol + push!(syms, b) + return + @case ::Symbol + push!(syms, ex) + return + @case _ + return + end +end + +function get_type_parameters(args :: AbstractArray{T, 1})::AbstractSet{Symbol} where T + syms = Set{Symbol}() + for arg in args + take_type_parameters!(syms, arg) + end + syms +end + +function get_type_parameters_ordered(args :: AbstractArray{T, 1})::Vector{Symbol} where T + syms = Symbol[] + for arg in args + take_type_parameters!(syms, arg) + end + unique!(syms) + syms +end + +macro reexport(m) + m = __module__.eval(m) + ns = names(m) + isempty(ns) ? nothing : esc(:(export $(ns...))) +end + +end diff --git a/src/Extension.jl b/src/Extension.jl index 7bbbef3..f317798 100644 --- a/src/Extension.jl +++ b/src/Extension.jl @@ -1,52 +1,24 @@ module Extension using MLStyle.Err - -Support = Dict{Symbol, Vector{Module}}( - :GADT => [], - :Enum => [], - :UppercaseCapturing => [], -) - -Check = Dict{Symbol, Function}( - :UppercaseCapturing => function (mod :: Module) - if used(:Enum, mod) - throw("Cannot use extensions `UppercaseCapturing` and `Enum` simultaneously.") - end - end, - :Enum => function (mod :: Module) - if used(:UppercaseCapturing, mod) - throw("Cannot use extensions `UppercaseCapturing` and `Enum` simultaneously.") - end - end -) export use, @use, used function used(ext :: Symbol, mod :: Module) :: Bool - get(Support, ext) do - throw(UnknownExtension(ext)) - end |> mods -> mod in mods + Base.depwarn( + "No need to use this function anymore since MLStyle v0.4", + :used + ) + false end -_donothing(_) = nothing - function use(ext :: Symbol, mod :: Module) - mods = get(Support, ext) do - Support[ext] = [] - end - - check_extension = get(Check, ext) do - _donothing - end - check_extension(mod) - - push!(mods, mod) + Base.depwarn( + "No need to use this function anymore since MLStyle v0.4", + :use + ) end macro use(exts...) - mod = __module__ - for each in exts - use(each, mod) - end + use(:_, @__MODULE__) end end \ No newline at end of file diff --git a/src/Infras.jl b/src/Infras.jl deleted file mode 100644 index 2c46c47..0000000 --- a/src/Infras.jl +++ /dev/null @@ -1,1693 +0,0 @@ -# This file is automatically generated by MLStyle Bootstrap Tools. -module Infras -using MLStyle -@eval $(LineNumberNode( - 2, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(LineNumberNode( - 3, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :using, - Expr( - :., - :MLStyle, - :MatchCore, - ), -)) -@eval $(LineNumberNode( - 4, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :using, - Expr( - :., - :MLStyle, - :Extension, - ), -)) -@eval $(LineNumberNode( - 5, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :using, - Expr( - :., - :MLStyle, - :Err, - ), -)) -@eval $(LineNumberNode( - 6, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :using, - Expr( - :(:), - Expr( - :., - :MLStyle, - :Toolz, - ), - Expr( - :., - :$, - ), - ), -)) -@eval $(LineNumberNode( - 7, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :using, - Expr( - :(:), - Expr( - :., - :MLStyle, - :Render, - ), - Expr( - :., - :render, - ), - Expr( - :., - :format, - ), - ), -)) -@eval $(LineNumberNode( - 9, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :const, - Expr( - :(=), - :__L__, - Expr( - :macrocall, - Symbol("@__LINE__"), - LineNumberNode( - 9, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - ), - ), -)) -@eval $(LineNumberNode( - 10, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - Symbol("@format"), -)) -@eval $(LineNumberNode( - 11, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :macro, - Expr( - :call, - :format, - :args, - :template, - ), - Expr( - :block, - LineNumberNode( - 12, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :args, - Expr( - :call, - :Expr, - QuoteNode( - :vect, - ), - Expr( - :call, - :(=>), - QuoteNode( - :__L__, - ), - Expr( - :call, - :LineNumberNode, - :__L__, - ), - ), - Expr( - :call, - :(=>), - QuoteNode( - :failed, - ), - Expr( - :call, - :QuoteNode, - Expr( - :quote, - Expr( - :., - Expr( - :$, - :MatchCore, - ), - QuoteNode( - :failed, - ), - ), - ), - ), - ), - Expr( - :..., - Expr( - :., - :args, - QuoteNode( - :args, - ), - ), - ), - ), - ), - LineNumberNode( - 13, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :esc, - Expr( - :call, - :format, - :args, - :template, - ), - ), - ), -)) -@eval $(LineNumberNode( - 16, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - Symbol("@typed_as"), -)) -@eval $(LineNumberNode( - 17, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :macrocall, - GlobalRef( - Core, - Symbol("@doc"), - ), - LineNumberNode( - 17, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - "this macro provide a convenient and type-stable way to generate\ntype checking.\n\nHowever, the generated code requires a given variable `TARGET`\nin metaprogramming level.\n\ne.g\n\nTARGET = :target_id\n(@typed_as Int) # which generates a pattern to check if type is Int.\n\n", - Expr( - :macro, - Expr( - :call, - :typed_as, - :t, - ), - Expr( - :block, - LineNumberNode( - 31, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :$, - :esc, - Expr( - :quote, - Expr( - :block, - LineNumberNode( - 32, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :NAME, - Expr( - :call, - :mangle, - :mod, - ), - ), - LineNumberNode( - 33, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :__T__, - Expr( - :$, - :t, - ), - ), - LineNumberNode( - 34, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :function, - Expr( - :tuple, - :body, - ), - Expr( - :block, - LineNumberNode( - 35, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :macrocall, - Symbol("@format"), - LineNumberNode( - 35, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :vect, - :body, - :tag, - :TARGET, - :NAME, - :__T__, - ), - Expr( - :quote, - Expr( - :block, - LineNumberNode( - 37, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :macrocall, - Symbol("@inline"), - LineNumberNode( - 37, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - :__L__, - Expr( - :function, - Expr( - :call, - :NAME, - Expr( - :(::), - :TARGET, - :__T__, - ), - ), - Expr( - :block, - LineNumberNode( - 38, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - :__T__, - LineNumberNode( - 39, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - :body, - ), - ), - ), - LineNumberNode( - 42, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :macrocall, - Symbol("@inline"), - LineNumberNode( - 42, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - :__L__, - Expr( - :function, - Expr( - :call, - :NAME, - :TARGET, - ), - Expr( - :block, - LineNumberNode( - 43, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - :failed, - ), - ), - ), - LineNumberNode( - 46, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :NAME, - :tag, - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 52, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - :patternOr, -)) -@eval $(LineNumberNode( - 53, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :(=), - :patternOr, - Expr( - :->, - Expr( - :tuple, - :p1, - :p2, - ), - Expr( - :block, - LineNumberNode( - 53, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :->, - :body, - Expr( - :block, - LineNumberNode( - 53, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :let, - Expr( - :block, - Expr( - :(=), - :p1, - Expr( - :call, - :p1, - :body, - ), - ), - Expr( - :(=), - :p2, - Expr( - :call, - :p2, - :body, - ), - ), - ), - Expr( - :block, - LineNumberNode( - 55, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :tmp, - Expr( - :call, - :mangle, - :Infras, - ), - ), - LineNumberNode( - 56, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :macrocall, - Symbol("@format"), - LineNumberNode( - 56, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :vect, - :tmp, - :p1, - :p2, - ), - Expr( - :quote, - Expr( - :block, - LineNumberNode( - 57, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :tmp, - :p1, - ), - LineNumberNode( - 58, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :if, - Expr( - :call, - :(===), - :tmp, - :failed, - ), - :p2, - :tmp, - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 63, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :const, - Expr( - :(=), - :APP_DESTRUCTORS, - Expr( - :call, - Expr( - :curly, - :Vector, - Expr( - :curly, - :Tuple, - :Module, - :PDesc, - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 64, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :const, - Expr( - :(=), - :GAPP_DESTRUCTORS, - Expr( - :call, - Expr( - :curly, - :Vector, - Expr( - :curly, - :Tuple, - :Module, - :PDesc, - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 66, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - :def_pattern, -)) -@eval $(LineNumberNode( - 67, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :def_pattern, - Expr( - :parameters, - :predicate, - :rewrite, - Expr( - :kw, - :qualifiers, - :nothing, - ), - ), - :mod, - ), - Expr( - :block, - LineNumberNode( - 68, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :qualifiers, - Expr( - :if, - Expr( - :call, - :(===), - :qualifiers, - :nothing, - ), - Expr( - :call, - :Set, - Expr( - :vect, - :invasive, - ), - ), - :qualifiers, - ), - ), - LineNumberNode( - 69, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :desc, - Expr( - :call, - :PDesc, - :predicate, - :rewrite, - :qualifiers, - ), - ), - LineNumberNode( - 70, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :register_pattern, - :desc, - :mod, - ), - ), -)) -@eval $(LineNumberNode( - 76, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :macrocall, - GlobalRef( - Core, - Symbol("@doc"), - ), - LineNumberNode( - 76, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - " To test if an datatype can\n", - Expr( - :function, - :deconstructable, - ), -)) -@eval $(LineNumberNode( - 82, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - :register_app_pattern, -)) -@eval $(LineNumberNode( - 83, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :register_app_pattern, - Expr( - :(::), - :pdesc, - :PDesc, - ), - Expr( - :(::), - :def_mod, - :Module, - ), - ), - Expr( - :block, - LineNumberNode( - 84, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :push!, - :APP_DESTRUCTORS, - Expr( - :tuple, - :def_mod, - :pdesc, - ), - ), - ), -)) -@eval $(LineNumberNode( - 87, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - :def_app_pattern, -)) -@eval $(LineNumberNode( - 88, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :def_app_pattern, - Expr( - :parameters, - :predicate, - :rewrite, - Expr( - :kw, - :qualifiers, - :nothing, - ), - ), - :mod, - ), - Expr( - :block, - LineNumberNode( - 89, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :qualifiers, - Expr( - :if, - Expr( - :call, - :(===), - :qualifiers, - :nothing, - ), - Expr( - :call, - :Set, - Expr( - :vect, - :invasive, - ), - ), - :qualifiers, - ), - ), - LineNumberNode( - 90, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :desc, - Expr( - :call, - :PDesc, - :predicate, - :rewrite, - :qualifiers, - ), - ), - LineNumberNode( - 91, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :register_app_pattern, - :desc, - :mod, - ), - ), -)) -@eval $(LineNumberNode( - 94, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - :mk_app_pattern, -)) -@eval $(LineNumberNode( - 95, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :mk_app_pattern, - :tag, - :hd, - :tl, - :use_mod, - ), - Expr( - :block, - LineNumberNode( - 96, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :if, - Expr( - :call, - :isdefined, - :use_mod, - :hd, - ), - Expr( - :block, - LineNumberNode( - 97, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :hd, - Expr( - :call, - :getfield, - :use_mod, - :hd, - ), - ), - LineNumberNode( - 98, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :for, - Expr( - :(=), - Expr( - :tuple, - :def_mod, - :desc, - ), - :APP_DESTRUCTORS, - ), - Expr( - :block, - LineNumberNode( - 99, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :if, - Expr( - :&&, - Expr( - :call, - :qualifier_test, - Expr( - :., - :desc, - QuoteNode( - :qualifiers, - ), - ), - :use_mod, - :def_mod, - ), - Expr( - :call, - Expr( - :., - :desc, - QuoteNode( - :predicate, - ), - ), - :hd, - :tl, - ), - ), - Expr( - :block, - LineNumberNode( - 100, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :return, - Expr( - :call, - Expr( - :., - :desc, - QuoteNode( - :rewrite, - ), - ), - :tag, - :hd, - :tl, - :use_mod, - ), - ), - ), - ), - ), - ), - ), - ), - LineNumberNode( - 104, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :info, - Expr( - :call, - :*, - Expr( - :call, - :string, - :hd, - ), - "(", - Expr( - :call, - :join, - Expr( - :call, - :map, - :string, - :tl, - ), - ", ", - ), - ")", - ), - ), - LineNumberNode( - 105, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :$, - :throw, - Expr( - :call, - :PatternUnsolvedException, - Expr( - :string, - "invalid usage or unknown application case ", - :info, - ".", - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 112, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - :def_gapp_pattern, -)) -@eval $(LineNumberNode( - 113, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :def_gapp_pattern, - Expr( - :parameters, - :predicate, - :rewrite, - Expr( - :kw, - :qualifiers, - :nothing, - ), - ), - :mod, - ), - Expr( - :block, - LineNumberNode( - 114, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :qualifiers, - Expr( - :if, - Expr( - :call, - :(===), - :qualifiers, - :nothing, - ), - Expr( - :call, - :Set, - Expr( - :vect, - :invasive, - ), - ), - :qualifiers, - ), - ), - LineNumberNode( - 115, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - :desc, - Expr( - :call, - :PDesc, - :predicate, - :rewrite, - :qualifiers, - ), - ), - LineNumberNode( - 116, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :register_gapp_pattern, - :desc, - :mod, - ), - ), -)) -@eval $(LineNumberNode( - 119, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - :register_gapp_pattern, -)) -@eval $(LineNumberNode( - 120, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :register_gapp_pattern, - Expr( - :(::), - :pdesc, - :PDesc, - ), - Expr( - :(::), - :def_mod, - :Module, - ), - ), - Expr( - :block, - LineNumberNode( - 121, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - :push!, - :GAPP_DESTRUCTORS, - Expr( - :tuple, - :def_mod, - :pdesc, - ), - ), - ), -)) -@eval $(LineNumberNode( - 124, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :export, - :mk_gapp_pattern, -)) -@eval $(LineNumberNode( - 125, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :function, - :mk_gapp_pattern, -)) -@eval $(LineNumberNode( - 130, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), -)) -@eval $(Expr( - :call, - :def_pattern, - :Infras, - Expr( - :kw, - :predicate, - Expr( - :->, - :x, - Expr( - :block, - LineNumberNode( - 131, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :&&, - Expr( - :call, - :isa, - :x, - :Expr, - ), - Expr( - :call, - :(==), - Expr( - :., - :x, - QuoteNode( - :head, - ), - ), - QuoteNode( - :call, - ), - ), - ), - ), - ), - ), - Expr( - :kw, - :rewrite, - Expr( - :->, - Expr( - :tuple, - :tag, - :case, - :mod, - ), - Expr( - :block, - LineNumberNode( - 132, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :let, - Expr( - :block, - Expr( - :(=), - :hd, - Expr( - :ref, - Expr( - :., - :case, - QuoteNode( - :args, - ), - ), - 1, - ), - ), - Expr( - :(=), - :tl, - Expr( - :ref, - Expr( - :., - :case, - QuoteNode( - :args, - ), - ), - Expr( - :call, - :(:), - 2, - :end, - ), - ), - ), - ), - Expr( - :block, - LineNumberNode( - 134, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :block, - Expr( - :let, - Expr( - :(=), - Symbol("##Main.MLBootstrap 1#398"), - :hd, - ), - Expr( - :block, - Expr( - :block, - LineNumberNode( - 135, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - Symbol("##Main.MLBootstrap 0#397"), - Expr( - :block, - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 14#411"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 13#410"), - :Symbol, - ), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - :Symbol, - Expr( - :call, - :mk_app_pattern, - :tag, - :hd, - :tl, - :mod, - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 14#411"), - Symbol("##Main.MLBootstrap 13#410"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 14#411"), - Symbol("##Main.MLBootstrap 1#398"), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##Main.MLBootstrap 0#397"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - LineNumberNode( - 136, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - Symbol("##Main.MLBootstrap 0#397"), - Expr( - :block, - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 5#402"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 2#399"), - Expr, - ), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr, - Expr( - :block, - Expr( - :let, - Expr( - :(=), - Expr( - :tuple, - Symbol("##Main.MLBootstrap 3#400"), - Symbol("##Main.MLBootstrap 4#401"), - ), - Expr( - :tuple, - Expr( - :., - Symbol("##Main.MLBootstrap 2#399"), - QuoteNode( - :head, - ), - ), - Expr( - :., - Symbol("##Main.MLBootstrap 2#399"), - QuoteNode( - :args, - ), - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :if, - Expr( - :call, - ===, - Symbol("##Main.MLBootstrap 3#400"), - QuoteNode( - :curly, - ), - ), - Expr( - :block, - Expr( - :function, - Expr( - :where, - Expr( - :call, - Symbol("##Main.MLBootstrap 7#404"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 6#403"), - Expr( - :curly, - AbstractArray, - Symbol("##Main.MLBootstrap 8#405"), - 1, - ), - ), - ), - Symbol("##Main.MLBootstrap 8#405"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :block, - Expr( - :if, - Expr( - :call, - :>=, - Expr( - :call, - length, - Symbol("##Main.MLBootstrap 6#403"), - ), - 1, - ), - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 9#406"), - Expr( - :ref, - Symbol("##Main.MLBootstrap 6#403"), - 1, - ), - ), - Expr( - :block, - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 11#408"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 10#407"), - :Symbol, - ), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - :Symbol, - Expr( - :call, - :mk_gapp_pattern, - :tag, - Expr( - :vect, - ), - :hd, - :tl, - :mod, - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 11#408"), - Symbol("##Main.MLBootstrap 10#407"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 11#408"), - Symbol("##Main.MLBootstrap 9#406"), - ), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 7#404"), - :_, - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 7#404"), - Symbol("##Main.MLBootstrap 4#401"), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 5#402"), - Symbol("##Main.MLBootstrap 2#399"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 5#402"), - Symbol("##Main.MLBootstrap 1#398"), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##Main.MLBootstrap 0#397"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - LineNumberNode( - 137, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :(=), - Symbol("##Main.MLBootstrap 0#397"), - Expr( - :call, - :throw, - Expr( - :call, - :error, - Expr( - :string, - "Deconstructor cannot be an expression like ", - :hd, - ".", - ), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##Main.MLBootstrap 0#397"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - LineNumberNode( - 134, - Symbol("/home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl"), - ), - Expr( - :call, - throw, - Expr( - :call, - InternalException, - "Non-exhaustive pattern found, at #= /home/redbq/github2/MLStyle.jl/bootstrap/Infras.jl:134 =#!", - ), - ), - ), - Symbol("##Main.MLBootstrap 0#397"), - ), - ), - Symbol("##Main.MLBootstrap 0#397"), - ), - ), - Symbol("##Main.MLBootstrap 0#397"), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), -)) -end diff --git a/src/Internal/InternalList.jl b/src/Internal/InternalList.jl deleted file mode 100644 index 2acbdf1..0000000 --- a/src/Internal/InternalList.jl +++ /dev/null @@ -1,57 +0,0 @@ -module List -import Base: length, (:), map, iterate, reverse, filter -export cons, nil, linkedlist - -# if generic: -# map(f, l :: nil{T} ) where T = nil() - -abstract type linkedlist end - -struct cons <: linkedlist - head - tail :: linkedlist -end - -struct nil <: linkedlist end - -length(l :: nil) = 0 -length(l :: cons) = 1 + length(l.tail) - -head(l :: nil ) = nothing -head(l :: cons) = l.head - -tail(l :: nil ) = nothing -tail(l :: cons) = l.tail - -iter(f, l :: nil ) = nothing -iter(f, l :: cons) = begin f(l.head); iter(f, l.tail) end - -map(f, l :: nil ) = nil() -map(f, l :: cons) = cons(f(l.head), map(f, l.tail)) - -function filter(f :: Function, l :: linkedlist) - l2 = nil() - for h in l - if f(h) - l2 = cons(h, l2) - end - end - reverse(l2) -end - -iterate(l :: linkedlist, ::nil) = nothing -function iterate(l :: linkedlist, state::cons = l) - state.head, state.tail -end - -function reverse(l :: linkedlist) - l2 = nil() - for hd in l - l2 = cons(hd, l2) - end - l2 -end - -end - - diff --git a/src/Internal/Toolz.jl b/src/Internal/Toolz.jl deleted file mode 100644 index 3d6bf42..0000000 --- a/src/Internal/Toolz.jl +++ /dev/null @@ -1,9 +0,0 @@ -module Toolz -include("InternalList.jl") -using .List: head, tail, cons, nil, reverse, linkedlist - -export ($), isCapitalized - -($)(f, a) = f(a) -isCapitalized(s :: AbstractString) :: Bool = !isempty(s) && isuppercase(s[1]) -end diff --git a/src/MLStyle.jl b/src/MLStyle.jl index 308a7f1..cba0162 100644 --- a/src/MLStyle.jl +++ b/src/MLStyle.jl @@ -1,70 +1,55 @@ module MLStyle - -# Flags -export @use, use, @used -# Match Implementation -export @match, gen_match -# DataTypes -export @data - -# Pervasive Patterns -export Many, Do -# Active Patterns -export @active -# Extensibilities -export def_pattern, def_app_pattern, def_gapp_pattern, mk_pattern, mk_app_pattern, mk_gapp_pattern, def_record, def_active_pattern -# Exceptions -export PatternUnsolvedException, InternalException, SyntaxError, UnknownExtension, @syntax_err -# Syntax Sugars -export @as_record -export @λ, gen_lambda -export @when, @otherwise, gen_when - -# convenient modules -export Modules - - include("Err.jl") -using MLStyle.Err +using .Err +# ================deprecated=============== include("Extension.jl") -using MLStyle.Extension - -include("Internal/Toolz.jl") - +include("Qualification.jl") include("Render.jl") +# ========================================= +include("AbstractPatterns/AbstractPattern.jl") include("MatchCore.jl") -using MLStyle.MatchCore - -include("Infras.jl") -using MLStyle.Infras +include("ExprTools.jl") +include("MatchImpl.jl") +include("Record.jl") +include("DataType.jl") -include("Pervasives.jl") -using MLStyle.Pervasives +using .ExprTools -include("Qualification.jl") +@reexport Err -include("TypeVarExtraction.jl") +using .DataType +@reexport DataType -include("StandardPatterns/TypeVarDecons.jl") -include("StandardPatterns/Active.jl") -using MLStyle.Active +using .MatchImpl +@reexport MatchImpl -include("Record.jl") -using MLStyle.Record +using .Record +@reexport Record -include("DataType.jl") -using MLStyle.DataType +include("Pervasives.jl") +using .Pervasives: Do, Many, GuardBy +export Do, Many, GuardBy -include("StandardPatterns/Uncomprehensions.jl") +include("Sugars.jl") include("StandardPatterns/LambdaCases.jl") -using MLStyle.LambdaCases +using .LambdaCases +@reexport LambdaCases + +include("StandardPatterns/Active.jl") +using .Active +@reexport Active include("StandardPatterns/WhenCases.jl") -using MLStyle.WhenCases +using .WhenCases +@reexport WhenCases + +using .Extension +@reexport Extension include("Modules/Modules.jl") +export Modules end # module diff --git a/src/MatchCore-Deprecated.jl b/src/MatchCore-Deprecated.jl deleted file mode 100644 index 67ac393..0000000 --- a/src/MatchCore-Deprecated.jl +++ /dev/null @@ -1,283 +0,0 @@ -module MatchCore -using MLStyle.Toolz.List -using MLStyle.Toolz -using MLStyle.Toolz: bind -using MLStyle.Err -using MLStyle.Render - -# a token to denote matching failure -export Failed, failed -struct Failed end -const failed = Failed() - -struct err_spec - location :: LineNumberNode - msg :: String -end - -struct config - loc :: LineNumberNode - errs :: linkedlist -end - -# lens of config. -# Variables named in snake_case are marked as internal use -loc(conf :: config) = conf.loc -errs(conf :: config) = conf.errs -set_loc(loc :: LineNumberNode) = (conf) -> config(loc, conf.errs) -set_errs(errs :: linkedlist) = (conf) -> config(conf.loc, errs) - -# some useful utils: -err!(msg :: String) = - bind(getBy $ loc) do loc - bind(getBy $ errs) do errs - lens = set_errs $ cons(err_spec(loc, msg), errs) - putBy(lens) - end - end - - -check_do(f, check_fn, err_fn) = expr -> - if !check_fn(expr) - err_fn(expr) - else - f(expr) - end - - -# TODO: for friendly error report -recog_err(expr) :: String = "syntax error" - -check_syntax(f, predicate) = begin - check_fn = expr -> predicate(expr) - err_fn = expr -> bind(err! ∘ recog_err $ expr) do _ - return! $ nothing - end - check_do(f, check_fn, err_fn) -end - -const init_state = config(LineNumberNode(1), nil()) - - -# implementation of qualified pattern matching - - -export Qualifier -Qualifier = Function - - -export internal, invasive, share_with, share_through - -internal = (my_mod, umod) -> my_mod === umod -invasive = (my_mod, umod) -> true -share_with(ms::Set{Module}) = (_, umod) -> umod in ms - -export qualifier_test -function qualifier_test(qualifiers :: Set{Qualifier}, use_mod, def_mod) - any(qualifiers) do q - q(def_mod, use_mod) - end -end - -export PDesc -struct PDesc - # for gapp patterns: (spec_vars :: Vector{Any}, gapobject :: Object, tl :: Vector{AST}) -> bool - # for app patterns: (appobject :: Object, tl :: Vector{AST}) -> bool - # for general : (case:AST) -> bool - - predicate :: Function - - # for gapp: - # (to_match : Symbol, - # forall :: Vector{Any}, - # spec_vars :: Vector{Any}, - # appobject :: Object, - # args :: Vector{AST}, - # mod :: Module) -> (AST -> AST) - # for app: (to_match : Symbol, appobject: Object, args::Vector{AST}, mod::Module) -> (AST -> AST) - # general: (to_match : Symbol, case:AST, mod :: Module) -> (AST -> AST) - rewrite :: Function - - qualifiers :: Set{Qualifier} -end - -PDesc(;predicate, rewrite, qualifiers) = - PDesc(predicate, rewrite, qualifiers) - -# A bug occurred when key is of Module type. -# Tentatively we use associate list(vector?). - -const PATTERNS = Vector{Tuple{Module, PDesc}}() - -export register_pattern - -function register_pattern(pdesc :: PDesc, defmod :: Module) - push!(PATTERNS, (defmod, pdesc)) -end - -""" -A simple example to define pattern `1`: - -```julia - tp = PDesc( - x -> x === 1, - (tag, case, mod) -> body -> - @format [] - :(\$s == 1 ? body : failed) - end, - Set([invasive]) - ) - register_pattern(tp, MatchCore) -``` -""" -function get_pattern(case, use_mod :: Module) - for (def_mod, desc) in PATTERNS - if qualifier_test(desc.qualifiers, use_mod, def_mod) && desc.predicate(case) - return desc.rewrite - end - end -end - - -is_head_eq(s :: Symbol) = (e::Expr) -> e.head == s - -function collect_cases(expr :: Expr) :: State - expr |> - check_syntax(is_head_eq(:block)) do block - bind(forM(collect_case, block.args)) do cases - return! $ filter(a -> a !== nothing, cases) - end - end -end - -function collect_case(expr :: LineNumberNode) :: State - bind(putBy ∘ set_loc $ expr) do _ - return! $ nothing - end -end - -function collect_case(expr :: Expr) :: State - expr |> - check_syntax(is_head_eq(:call)) do expr - expr.args |> - check_syntax(args -> - length(args) == 3 && - args[1] == :(=>)) do (_, case, body) - bind(getBy $ loc) do loc - return! $ (loc, case, body) - end - end - end -end - -const INTERNAL_COUNTER = Dict{Module, Int}() - -function remove_module_patterns(mod :: Module) - delete!(INTERNAL_COUNTER, mod) -end - -function get_name_of_module(m::Module) :: String - string(m) -end - - -export mangle -""" -Allocate names for anonymous temporary variables. -""" -function mangle(mod::Module) - get!(INTERNAL_COUNTER, mod) do - 0 - end |> id -> begin - INTERNAL_COUNTER[mod] = id + 1 - mod_name = get_name_of_module(mod) - gensym("$mod_name $id") - end - -end - - -function match_impl(target, cbl, mod) - # cbl: case body list - # cbl = (fst ∘ (flip $ runState $ init_state) ∘ collectCases) $ cbl - bind(collect_cases(cbl)) do cbl - # cbl :: [(LineNumberNodem, Expr, Expr)] - tag_sym = mangle(mod) - mk_match_body(target, tag_sym, cbl, mod) - end -end - - -throw_from(errs) = begin - # TODO: pretty print - s = string(errs) - throw(SyntaxError("$s")) -end - -export @match, gen_match - -function gen_match(target, cbl, mod) - (a, s) = runState $ match_impl(target, cbl, mod) $ init_state - if isempty(s.errs) - a - else - throw_from(s.errs) - end -end - -""" -```julia -@match target begin - pattern1 => body1 - pattern2 => body2 - ... -end -``` -""" -macro match(target, cbl) - gen_match(target, cbl, __module__) |> esc -end - -function mk_match_body(target, tag_sym, cbl, mod) - bind(getBy $ loc) do loc # start 1 - final = - @format [loc, throw, InternalException] quote - loc - throw(InternalException("Non-exhaustive pattern found!")) - end - result = mangle(mod) - cbl = collect(cbl) - main_logic = - foldr(cbl, init=final) do (loc, case, body), last # start 2 - expr = mk_pattern(tag_sym, case, mod)(body) - @format [ - result, - expr, - loc, - MatchCore, - last - ] quote - loc - result = expr - result === $MatchCore.failed ? last : result - end - end # end 2 - return! $ @format [tag_sym, target, main_logic] quote - let tag_sym = target - main_logic - end - end - end # end 1 -end - -export mk_pattern -function mk_pattern(tag_sym :: Symbol, case :: Any, mod :: Module) - rewrite = get_pattern(case, mod) - if rewrite !== nothing - return rewrite(tag_sym, case, mod) - end - case = string(case) - throw $ PatternUnsolvedException("invalid usage or unknown case $case") -end - -end # module end diff --git a/src/MatchCore.jl b/src/MatchCore.jl index 25fb0b2..745d276 100644 --- a/src/MatchCore.jl +++ b/src/MatchCore.jl @@ -1,2540 +1,228 @@ -# This file is automatically generated by MLStyle Bootstrap Tools. module MatchCore using MLStyle -@eval $(LineNumberNode( - 2, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(LineNumberNode( - 3, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :using, - Expr( - :., - :MLStyle, - ), -)) -@eval $(LineNumberNode( - 4, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :using, - Expr( - :., - :MLStyle, - :Toolz, - :List, - ), -)) -@eval $(LineNumberNode( - 5, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :using, - Expr( - :., - :MLStyle, - :Err, - ), -)) -@eval $(LineNumberNode( - 6, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :using, - Expr( - :., - :MLStyle, - :Render, - ), -)) -@eval $(LineNumberNode( - 9, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :Failed, - :failed, -)) -@eval $(LineNumberNode( - 10, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :struct, - false, - :Failed, - Expr( - :block, - LineNumberNode( - 10, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - ), -)) -@eval $(LineNumberNode( - 11, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :const, - Expr( - :(=), - :failed, - Expr( - :call, - :Failed, - ), - ), -)) -@eval $(LineNumberNode( - 13, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :Qualifier, -)) -@eval $(LineNumberNode( - 14, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :(=), - :Qualifier, - :Function, -)) -@eval $(LineNumberNode( - 17, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :internal, - :invasive, - :share_with, - :share_through, -)) -@eval $(LineNumberNode( - 19, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :(=), - :internal, - Expr( - :->, - Expr( - :tuple, - :my_mod, - :umod, - ), - Expr( - :block, - LineNumberNode( - 19, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :(===), - :my_mod, - :umod, - ), - ), - ), -)) -@eval $(LineNumberNode( - 20, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :(=), - :invasive, - Expr( - :->, - Expr( - :tuple, - :my_mod, - :umod, - ), - Expr( - :block, - LineNumberNode( - 20, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - true, - ), - ), -)) -@eval $(LineNumberNode( - 21, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :(=), - Expr( - :call, - :share_with, - Expr( - :(::), - :ms, - Expr( - :curly, - :Set, - :Module, - ), - ), - ), - Expr( - :block, - LineNumberNode( - 21, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :->, - Expr( - :tuple, - :_, - :umod, - ), - Expr( - :block, - LineNumberNode( - 21, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :in, - :umod, - :ms, - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 23, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :qualifier_test, -)) -@eval $(LineNumberNode( - 24, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :qualifier_test, - Expr( - :(::), - :qualifiers, - Expr( - :curly, - :Set, - :Qualifier, - ), - ), - :use_mod, - :def_mod, - ), - Expr( - :block, - LineNumberNode( - 25, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :do, - Expr( - :call, - :any, - :qualifiers, - ), - Expr( - :->, - Expr( - :tuple, - :q, - ), - Expr( - :block, - LineNumberNode( - 26, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :q, - :def_mod, - :use_mod, - ), - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 30, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :PDesc, -)) -@eval $(LineNumberNode( - 31, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :struct, - false, - :PDesc, - Expr( - :block, - LineNumberNode( - 36, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(::), - :predicate, - :Function, - ), - LineNumberNode( - 47, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(::), - :rewrite, - :Function, - ), - LineNumberNode( - 49, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(::), - :qualifiers, - Expr( - :curly, - :Set, - :Qualifier, - ), - ), - ), -)) -@eval $(LineNumberNode( - 52, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :(=), - Expr( - :call, - :PDesc, - Expr( - :parameters, - :predicate, - :rewrite, - :qualifiers, - ), - ), - Expr( - :block, - LineNumberNode( - 52, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :PDesc, - :predicate, - :rewrite, - :qualifiers, - ), - ), -)) -@eval $(LineNumberNode( - 58, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :const, - Expr( - :(=), - :PATTERNS, - Expr( - :call, - Expr( - :curly, - :Vector, - Expr( - :curly, - :Tuple, - :Module, - :PDesc, - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 60, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :register_pattern, -)) -@eval $(LineNumberNode( - 62, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :register_pattern, - Expr( - :(::), - :pdesc, - :PDesc, - ), - Expr( - :(::), - :defmod, - :Module, - ), - ), - Expr( - :block, - LineNumberNode( - 63, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :push!, - :PATTERNS, - Expr( - :tuple, - :defmod, - :pdesc, - ), - ), - ), -)) -@eval $(LineNumberNode( - 65, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :get_pattern, - :case, - Expr( - :(::), - :use_mod, - :Module, - ), - ), - Expr( - :block, - LineNumberNode( - 66, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :for, - Expr( - :(=), - Expr( - :tuple, - :def_mod, - :desc, - ), - :PATTERNS, - ), - Expr( - :block, - LineNumberNode( - 67, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :if, - Expr( - :&&, - Expr( - :call, - :qualifier_test, - Expr( - :., - :desc, - QuoteNode( - :qualifiers, - ), - ), - :use_mod, - :def_mod, - ), - Expr( - :call, - Expr( - :., - :desc, - QuoteNode( - :predicate, - ), - ), - :case, - ), - ), - Expr( - :block, - LineNumberNode( - 68, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :return, - Expr( - :., - :desc, - QuoteNode( - :rewrite, - ), - ), - ), - ), - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 73, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :const, - Expr( - :(=), - :INTERNAL_COUNTER, - Expr( - :call, - Expr( - :curly, - :Dict, - :Module, - :Int, - ), - ), - ), -)) -@eval $(LineNumberNode( - 75, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :remove_module_patterns, - Expr( - :(::), - :mod, - :Module, - ), - ), - Expr( - :block, - LineNumberNode( - 76, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :delete!, - :INTERNAL_COUNTER, - :mod, - ), - ), -)) -@eval $(LineNumberNode( - 79, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :function, - Expr( - :(::), - Expr( - :call, - :get_name_of_module, - Expr( - :(::), - :m, - :Module, - ), - ), - :String, - ), - Expr( - :block, - LineNumberNode( - 80, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :string, - :m, - ), - ), -)) -@eval $(LineNumberNode( - 84, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :mangle, -)) -@eval $(LineNumberNode( - 85, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :mangle, - Expr( - :(::), - :mod, - :Module, - ), - ), - Expr( - :block, - LineNumberNode( - 86, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :|>, - Expr( - :do, - Expr( - :call, - :get!, - :INTERNAL_COUNTER, - :mod, - ), - Expr( - :->, - Expr( - :tuple, - ), - Expr( - :block, - LineNumberNode( - 87, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - 0, - ), - ), - ), - Expr( - :->, - :id, - Expr( - :block, - LineNumberNode( - 88, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - LineNumberNode( - 89, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - Expr( - :ref, - :INTERNAL_COUNTER, - :mod, - ), - Expr( - :call, - :+, - :id, - 1, - ), - ), - LineNumberNode( - 90, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :mod_name, - Expr( - :call, - :get_name_of_module, - :mod, - ), - ), - LineNumberNode( - 91, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :gensym, - Expr( - :string, - :mod_name, - " ", - :id, - ), - ), - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 96, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :gen_match, - Symbol("@match"), -)) -@eval $(LineNumberNode( - 97, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :macro, - Expr( - :call, - :match, - :target, - :cbl, - ), - Expr( - :block, - LineNumberNode( - 98, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :|>, - Expr( - :call, - :gen_match, - :target, - :cbl, - :__source__, - :__module__, - ), - :esc, - ), - ), -)) -@eval $(LineNumberNode( - 101, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :gen_match, - :target, - :cbl, - Expr( - :(::), - :init_loc, - :LineNumberNode, - ), - Expr( - :(::), - :mod, - :Module, - ), - ), - Expr( - :block, - LineNumberNode( - 102, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :branches, - Expr( - :block, - Expr( - :block, - Expr( - :let, - Expr( - :(=), - Symbol("##Main.MLBootstrap 1#549"), - :cbl, - ), - Expr( - :block, - Expr( - :block, - Expr( - :block, - LineNumberNode( - 103, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - Symbol("##Main.MLBootstrap 0#548"), - Expr( - :block, - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 5#553"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 2#550"), - Expr, - ), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr, - Expr( - :block, - Expr( - :let, - Expr( - :(=), - Expr( - :tuple, - Symbol("##Main.MLBootstrap 3#551"), - Symbol("##Main.MLBootstrap 4#552"), - ), - Expr( - :tuple, - Expr( - :., - Symbol("##Main.MLBootstrap 2#550"), - QuoteNode( - :head, - ), - ), - Expr( - :., - Symbol("##Main.MLBootstrap 2#550"), - QuoteNode( - :args, - ), - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :if, - Expr( - :call, - ===, - Symbol("##Main.MLBootstrap 3#551"), - QuoteNode( - :block, - ), - ), - Expr( - :block, - Expr( - :function, - Expr( - :where, - Expr( - :call, - Symbol("##Main.MLBootstrap 7#555"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 6#554"), - Expr( - :curly, - AbstractArray, - Symbol("##Main.MLBootstrap 8#556"), - 1, - ), - ), - ), - Symbol("##Main.MLBootstrap 8#556"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :block, - Expr( - :if, - Expr( - :call, - :>=, - Expr( - :call, - length, - Symbol("##Main.MLBootstrap 6#554"), - ), - 0, - ), - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 9#557"), - Expr( - :call, - :view, - Symbol("##Main.MLBootstrap 6#554"), - Expr( - :call, - :(:), - 1, - Expr( - :call, - :-, - Expr( - :call, - length, - Symbol("##Main.MLBootstrap 6#554"), - ), - 0, - ), - ), - ), - ), - Expr( - :block, - Expr( - :let, - Expr( - :(=), - :branches, - Symbol("##Main.MLBootstrap 9#557"), - ), - Expr( - :block, - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 11#559"), - true, - ), - Expr( - :for, - Expr( - :(=), - Symbol("##Main.MLBootstrap 10#558"), - Symbol("##Main.MLBootstrap 9#557"), - ), - Expr( - :block, - Expr( - :if, - Expr( - :call, - :!==, - Expr( - :block, - Expr( - :(=), - Symbol("##MLStyle.Infras 18#572"), - Expr( - :block, - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 15#563"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 12#560"), - Expr, - ), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr, - Expr( - :block, - Expr( - :let, - Expr( - :(=), - Expr( - :tuple, - Symbol("##Main.MLBootstrap 13#561"), - Symbol("##Main.MLBootstrap 14#562"), - ), - Expr( - :tuple, - Expr( - :., - Symbol("##Main.MLBootstrap 12#560"), - QuoteNode( - :head, - ), - ), - Expr( - :., - Symbol("##Main.MLBootstrap 12#560"), - QuoteNode( - :args, - ), - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :if, - Expr( - :call, - ===, - Symbol("##Main.MLBootstrap 13#561"), - QuoteNode( - :call, - ), - ), - Expr( - :block, - Expr( - :function, - Expr( - :where, - Expr( - :call, - Symbol("##Main.MLBootstrap 17#565"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 16#564"), - Expr( - :curly, - AbstractArray, - Symbol("##Main.MLBootstrap 18#566"), - 1, - ), - ), - ), - Symbol("##Main.MLBootstrap 18#566"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :block, - Expr( - :if, - Expr( - :call, - :(===), - Expr( - :call, - length, - Symbol("##Main.MLBootstrap 16#564"), - ), - 3, - ), - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 19#567"), - Expr( - :ref, - Symbol("##Main.MLBootstrap 16#564"), - 1, - ), - ), - Expr( - :block, - Expr( - :if, - Expr( - :call, - ===, - Symbol("##Main.MLBootstrap 19#567"), - QuoteNode( - :(=>), - ), - ), - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 20#568"), - Expr( - :ref, - Symbol("##Main.MLBootstrap 16#564"), - 2, - ), - ), - Expr( - :block, - Expr( - :let, - Expr( - :(=), - :a, - Symbol("##Main.MLBootstrap 20#568"), - ), - Expr( - :block, - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 21#569"), - Expr( - :ref, - Symbol("##Main.MLBootstrap 16#564"), - 3, - ), - ), - Expr( - :block, - Expr( - :let, - Expr( - :(=), - :b, - Symbol("##Main.MLBootstrap 21#569"), - ), - Expr( - :block, - nothing, - ), - ), - ), - ), - ), - ), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 17#565"), - :_, - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 17#565"), - Symbol("##Main.MLBootstrap 14#562"), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 15#563"), - Symbol("##Main.MLBootstrap 12#560"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 15#563"), - Symbol("##Main.MLBootstrap 10#558"), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##MLStyle.Infras 18#572"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 23#571"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 22#570"), - :LineNumberNode, - ), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - :LineNumberNode, - nothing, - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 23#571"), - Symbol("##Main.MLBootstrap 22#570"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 23#571"), - Symbol("##Main.MLBootstrap 10#558"), - ), - ), - Symbol("##MLStyle.Infras 18#572"), - ), - ), - :nothing, - ), - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 11#559"), - false, - ), - Expr( - :break, - ), - ), - ), - ), - ), - Expr( - :if, - Symbol("##Main.MLBootstrap 11#559"), - :branches, - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 7#555"), - :_, - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 7#555"), - Symbol("##Main.MLBootstrap 4#552"), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 5#553"), - Symbol("##Main.MLBootstrap 2#550"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 5#553"), - Symbol("##Main.MLBootstrap 1#549"), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##Main.MLBootstrap 0#548"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :block, - LineNumberNode( - 106, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - Symbol("##Main.MLBootstrap 0#548"), - Expr( - :call, - :throw, - Expr( - :call, - :SyntaxError, - Expr( - :call, - :*, - "Malformed syntax, expect `begin a => b; ... end` as match's branches., at ", - Expr( - :call, - :string, - :init_loc, - ), - ), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##Main.MLBootstrap 0#548"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :block, - LineNumberNode( - 102, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - throw, - Expr( - :call, - InternalException, - "Non-exhaustive pattern found, at #= /home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl:102 =#!", - ), - ), - ), - ), - ), - Expr( - :block, - Symbol("##Main.MLBootstrap 0#548"), - ), - ), - ), - ), - ), - Expr( - :block, - Symbol("##Main.MLBootstrap 0#548"), - ), - ), - ), - ), - ), - ), - ), - ), - ), - LineNumberNode( - 108, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :loc, - :init_loc, - ), - LineNumberNode( - 109, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :branches_located, - Expr( - :call, - :|>, - Expr( - :do, - Expr( - :call, - :map, - :branches, - ), - Expr( - :->, - Expr( - :tuple, - :each, - ), - Expr( - :block, - LineNumberNode( - 110, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :block, - Expr( - :block, - Expr( - :let, - Expr( - :(=), - Symbol("##Main.MLBootstrap 25#574"), - :each, - ), - Expr( - :block, - Expr( - :block, - Expr( - :block, - LineNumberNode( - 111, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - Symbol("##Main.MLBootstrap 24#573"), - Expr( - :block, - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 31#580"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 28#577"), - Expr, - ), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr, - Expr( - :block, - Expr( - :let, - Expr( - :(=), - Expr( - :tuple, - Symbol("##Main.MLBootstrap 29#578"), - Symbol("##Main.MLBootstrap 30#579"), - ), - Expr( - :tuple, - Expr( - :., - Symbol("##Main.MLBootstrap 28#577"), - QuoteNode( - :head, - ), - ), - Expr( - :., - Symbol("##Main.MLBootstrap 28#577"), - QuoteNode( - :args, - ), - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :if, - Expr( - :call, - ===, - Symbol("##Main.MLBootstrap 29#578"), - QuoteNode( - :call, - ), - ), - Expr( - :block, - Expr( - :function, - Expr( - :where, - Expr( - :call, - Symbol("##Main.MLBootstrap 33#582"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 32#581"), - Expr( - :curly, - AbstractArray, - Symbol("##Main.MLBootstrap 34#583"), - 1, - ), - ), - ), - Symbol("##Main.MLBootstrap 34#583"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :block, - Expr( - :if, - Expr( - :call, - :(===), - Expr( - :call, - length, - Symbol("##Main.MLBootstrap 32#581"), - ), - 3, - ), - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 35#584"), - Expr( - :ref, - Symbol("##Main.MLBootstrap 32#581"), - 1, - ), - ), - Expr( - :block, - Expr( - :if, - Expr( - :call, - ===, - Symbol("##Main.MLBootstrap 35#584"), - QuoteNode( - :(=>), - ), - ), - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 36#585"), - Expr( - :ref, - Symbol("##Main.MLBootstrap 32#581"), - 2, - ), - ), - Expr( - :block, - Expr( - :let, - Expr( - :(=), - :pattern, - Symbol("##Main.MLBootstrap 36#585"), - ), - Expr( - :block, - Expr( - :block, - Expr( - :(=), - Symbol("##Main.MLBootstrap 37#586"), - Expr( - :ref, - Symbol("##Main.MLBootstrap 32#581"), - 3, - ), - ), - Expr( - :block, - Expr( - :let, - Expr( - :(=), - :body, - Symbol("##Main.MLBootstrap 37#586"), - ), - Expr( - :block, - Expr( - :tuple, - :pattern, - :body, - :loc, - ), - ), - ), - ), - ), - ), - ), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 33#582"), - :_, - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 33#582"), - Symbol("##Main.MLBootstrap 30#579"), - ), - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 31#580"), - Symbol("##Main.MLBootstrap 28#577"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 31#580"), - Symbol("##Main.MLBootstrap 25#574"), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##Main.MLBootstrap 24#573"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :block, - LineNumberNode( - 113, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - Symbol("##Main.MLBootstrap 24#573"), - Expr( - :block, - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 27#576"), - Expr( - :(::), - Symbol("##Main.MLBootstrap 26#575"), - :LineNumberNode, - ), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - :LineNumberNode, - Expr( - :block, - Expr( - :let, - Expr( - :(=), - :curloc, - Symbol("##Main.MLBootstrap 26#575"), - ), - Expr( - :block, - Expr( - :block, - LineNumberNode( - 115, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :loc, - :curloc, - ), - LineNumberNode( - 116, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - :nothing, - ), - ), - ), - ), - ), - ), - Expr( - :function, - Expr( - :call, - Symbol("##Main.MLBootstrap 27#576"), - Symbol("##Main.MLBootstrap 26#575"), - ), - Expr( - :block, - Expr( - :meta, - :inline, - ), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - ), - Expr( - :call, - Symbol("##Main.MLBootstrap 27#576"), - Symbol("##Main.MLBootstrap 25#574"), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##Main.MLBootstrap 24#573"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :block, - LineNumberNode( - 118, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - Symbol("##Main.MLBootstrap 24#573"), - Expr( - :call, - :throw, - Expr( - :call, - :SyntaxError, - Expr( - :call, - :*, - "Malformed ast template, should be formed as `a => b`, at ", - Expr( - :call, - :string, - :last_lnode, - ), - ".", - ), - ), - ), - ), - Expr( - :if, - Expr( - :call, - :(===), - Symbol("##Main.MLBootstrap 24#573"), - Expr( - :., - MLStyle.MatchCore, - QuoteNode( - :failed, - ), - ), - ), - Expr( - :block, - Expr( - :block, - Expr( - :block, - LineNumberNode( - 110, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - throw, - Expr( - :call, - InternalException, - "Non-exhaustive pattern found, at #= /home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl:110 =#!", - ), - ), - ), - ), - ), - Expr( - :block, - Symbol("##Main.MLBootstrap 24#573"), - ), - ), - ), - ), - ), - Expr( - :block, - Symbol("##Main.MLBootstrap 24#573"), - ), - ), - ), - ), - ), - Expr( - :block, - Symbol("##Main.MLBootstrap 24#573"), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - ), - Expr( - :->, - :xs, - Expr( - :block, - LineNumberNode( - 120, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :filter, - Expr( - :->, - :x, - Expr( - :block, - LineNumberNode( - 120, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :!==, - :x, - :nothing, - ), - ), - ), - :xs, - ), - ), - ), - ), - ), - LineNumberNode( - 121, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :final, - Expr( - :let, - Expr( - :block, - Expr( - :(=), - :loc_str, - Expr( - :call, - :string, - :init_loc, - ), - ), - Expr( - :(=), - :exc_throw, - Expr( - :call, - :Expr, - QuoteNode( - :call, - ), - :InternalException, - Expr( - :call, - :*, - "Non-exhaustive pattern found, at ", - :loc_str, - "!", - ), - ), - ), - ), - Expr( - :block, - LineNumberNode( - 123, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :macrocall, - Symbol("@format"), - LineNumberNode( - 123, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :vect, - :init_loc, - :throw, - :exc_throw, - ), - Expr( - :quote, - Expr( - :block, - LineNumberNode( - 124, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - :init_loc, - LineNumberNode( - 125, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :throw, - :exc_throw, - ), - ), - ), - ), - ), - ), - ), - LineNumberNode( - 128, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :result, - Expr( - :call, - :mangle, - :mod, - ), - ), - LineNumberNode( - 129, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :tag_sym, - Expr( - :call, - :mangle, - :mod, - ), - ), - LineNumberNode( - 131, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :|>, - Expr( - :do, - Expr( - :call, - :foldr, - :branches_located, - Expr( - :kw, - :init, - :final, - ), - ), - Expr( - :->, - Expr( - :tuple, - Expr( - :tuple, - :pattern, - :body, - :loc, - ), - :last, - ), - Expr( - :block, - LineNumberNode( - 132, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :expr, - Expr( - :call, - Expr( - :call, - :mk_pattern, - :tag_sym, - :pattern, - :mod, - ), - :body, - ), - ), - LineNumberNode( - 133, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :macrocall, - Symbol("@format"), - LineNumberNode( - 133, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :vect, - :result, - :expr, - :loc, - :MatchCore, - :last, - ), - Expr( - :quote, - Expr( - :block, - LineNumberNode( - 140, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - :loc, - LineNumberNode( - 141, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :result, - :expr, - ), - LineNumberNode( - 142, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :if, - Expr( - :call, - :(===), - :result, - Expr( - :., - Expr( - :$, - :MatchCore, - ), - QuoteNode( - :failed, - ), - ), - ), - :last, - :result, - ), - ), - ), - ), - ), - ), - ), - Expr( - :->, - :main_logic, - Expr( - :block, - LineNumberNode( - 144, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :macrocall, - Symbol("@format"), - LineNumberNode( - 145, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :vect, - :tag_sym, - :target, - :main_logic, - ), - Expr( - :quote, - Expr( - :block, - LineNumberNode( - 146, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :let, - Expr( - :(=), - :tag_sym, - :target, - ), - Expr( - :block, - LineNumberNode( - 147, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - :main_logic, - ), - ), - ), - ), - ), - ), - ), - ), - ), -)) -@eval $(LineNumberNode( - 153, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :export, - :mk_pattern, -)) -@eval $(LineNumberNode( - 154, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), -)) -@eval $(Expr( - :function, - Expr( - :call, - :mk_pattern, - Expr( - :(::), - :tag_sym, - :Symbol, - ), - Expr( - :(::), - :case, - :Any, - ), - Expr( - :(::), - :mod, - :Module, - ), - ), - Expr( - :block, - LineNumberNode( - 155, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :rewrite, - Expr( - :call, - :get_pattern, - :case, - :mod, - ), - ), - LineNumberNode( - 156, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :if, - Expr( - :call, - :!==, - :rewrite, - :nothing, - ), - Expr( - :block, - LineNumberNode( - 157, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), +using MLStyle.Err + +export @sswitch, ellipsis_split, backend, P_partial_struct_decons +using MLStyle.AbstractPattern +using MLStyle.AbstractPattern.BasicPatterns + +""" +[a, b..., c] -> :vec3 => [a], b, [c] +[a, b, c] -> :vec => [a, b, c] +""" +function ellipsis_split(args::AbstractArray{T,1}) where {T} + ellipsis_index = findfirst(args) do arg + Meta.isexpr(arg, :...) + end + if ellipsis_index === nothing + Val(:vec) => args + else + Val(:vec3) => ( + args[1:ellipsis_index-1], + args[ellipsis_index].args[1], + args[ellipsis_index+1:end], + ) + end +end + +function qt2ex(ex::Any) + if ex isa Expr + Meta.isexpr(ex, :$) && return ex.args[1] Expr( - :return, - Expr( :call, - :rewrite, - :tag_sym, - :case, - :mod, - ), - ), - ), - ), - LineNumberNode( - 159, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :(=), - :case, - Expr( - :call, - :string, - :case, - ), - ), - LineNumberNode( - 160, - Symbol("/home/redbq/github/MLStyle.jl/bootstrap/MatchCore.jl"), - ), - Expr( - :call, - :throw, - Expr( - :call, - :PatternUnsolvedException, - Expr( - :string, - "invalid usage or unknown case ", - :case, - ), - ), - ), - ), -)) + Expr, + QuoteNode(ex.head), + Expr(:vect, (qt2ex(e) for e in ex.args if !(e isa LineNumberNode))...), + ) + elseif ex isa Symbol + QuoteNode(ex) + else + ex + end +end + + +const backend = MK(RedyFlavoured) + +function P_partial_struct_decons(t, partial_fields, ps, prepr::AbstractString = "$t") + function tcons(_...) + t + end + comp = PComp(prepr, tcons;) + function extract(sub, i::Int, ::Any, ::Any) + :($sub.$(partial_fields[i])) + end + decons(comp, extract, ps) +end + +basic_ex2tf(eval::Function, a) = + isprimitivetype(typeof(a)) ? literal(a) : error("invalid literal $a") +basic_ex2tf(eval::Function, l::LineNumberNode) = wildcard +basic_ex2tf(eval::Function, q::QuoteNode) = literal(q.value) +basic_ex2tf(eval::Function, s::String) = literal(s) +basic_ex2tf(eval::Function, n::Symbol) = n === :_ ? wildcard : P_capture(n) + +function basic_ex2tf(eval::Function, ex::Expr) + !(x) = basic_ex2tf(eval, x) + hd = ex.head + args = ex.args + n_args = length(args) + if hd === :|| + @assert n_args === 2 + l, r = args + or(!l, !r) + elseif hd === :&& + @assert n_args === 2 + l, r = args + and(!l, !r) + + elseif hd === :if + @assert n_args === 2 + let cond = args[1] + guard() do _, scope, _ + see_captured_vars(cond, scope) + end + end + elseif hd === :& + @assert n_args === 1 + val = args[1] + guard() do target, scope, _ + see_captured_vars(:($target == $val), scope) + end + elseif hd === :let + bind = args[1] + @assert bind isa Expr + if bind.head === :(=) + @assert bind.args[1] isa Symbol + P_bind(bind.args[1], bind.args[2], see_capture = true) + else + @assert bind.head === :block + binds = Function[ + P_bind(arg.args[1], arg.args[2], see_capture = true) for arg in bind.args + ] + push!(binds, wildcard) + and(binds) + end + elseif hd === :(::) + if n_args === 2 + p, ty = args + ty = eval(ty)::TypeObject + and(P_type_of(ty), !p) + else + @assert n_args === 1 + ty = args[1] + ty = eval(ty)::TypeObject + P_type_of(ty) + end + elseif hd === :vect + tag, split = ellipsis_split(args) + return tag isa Val{:vec} ? P_vector([!e for e in split]) : + let (init, mid, tail) = split + P_vector3([!e for e in init], !mid, [!e for e in tail]) + end + elseif hd === :tuple + P_tuple([!e for e in args]) + elseif hd === :call + let f = args[1], args′ = view(args, 2:length(args)) + n_args′ = n_args - 1 + t = eval(f) + if t === Core.svec + tag, split = ellipsis_split(args′) + return tag isa Val{:vec} ? P_svec([!e for e in split]) : + let (init, mid, tail) = split + P_svec3([!e for e in init], !mid, [!e for e in tail]) + end + end + all_field_ns = fieldnames(t) + partial_ns = Symbol[] + patterns = Function[] + if n_args′ >= 1 && Meta.isexpr(args′[1], :parameters) + kwargs = args′[1].args + args′ = view(args′, 2:length(args′)) + else + kwargs = [] + end + if length(all_field_ns) === length(args′) + append!(patterns, [!e for e in args′]) + append!(partial_ns, all_field_ns) + elseif length(partial_ns) !== 0 + error("count of positional fields should be 0 or the same as the fields($all_field_ns)") + end + for e in kwargs + if e isa Symbol + e in all_field_ns || + error("unknown field name $e for $t when field punnning.") + push!(partial_ns, e) + push!(patterns, P_capture(e)) + elseif Meta.isexpr(e, :kw) + key, value = e.args + key in all_field_ns || + error("unknown field name $key for $t when field punnning.") + @assert key isa Symbol + push!(partial_ns, key) + push!(patterns, and(P_capture(key), !value)) + end + end + P_partial_struct_decons(t, partial_ns, patterns) + end + elseif hd === :quote + !qt2ex(args[1]) + else + error("not implemented expr=>pattern rule for '($hd)' Expr.") + end +end + +const case_sym = Symbol("@case") +"""a minimal implementation of sswitch +this is incomplete and only for bootstrapping, do not use it. +""" +macro sswitch(val, ex) + @assert Meta.isexpr(ex, :block) + clauses = Union{LineNumberNode,Pair{<:Function,Symbol}}[] + body = Expr(:block) + alphabeta = 'a':'z' + k = 0 + ln = __source__ + variable_init_blocks = Dict{Symbol,Expr}() + for i in eachindex(ex.args) + stmt = ex.args[i] + if Meta.isexpr(stmt, :macrocall) && + stmt.args[1] === case_sym && + length(stmt.args) == 3 + + push!(clauses, ln) + k += 1 + pattern = try + basic_ex2tf(__module__.eval, stmt.args[3]) + catch e + e isa ErrorException && throw(PatternCompilationError(ln, e.msg)) + rethrow() + end + br::Symbol = Symbol(string(alphabeta[k%26]), k) + push!(clauses, pattern => br) + push!(body.args, CFGLabel(br)) + variable_init_block = Expr(:block) + push!(body.args, variable_init_block) + variable_init_blocks[br] = variable_init_block + else + if stmt isa LineNumberNode + ln = stmt + end + push!(body.args, stmt) + end + end + + terminal_scope, match_logic = backend(val, clauses, __source__) + for (br, branches_terminal_scope) in terminal_scope + variable_init = variable_init_blocks[br].args + for (actual_sym, mangled_sym) in branches_terminal_scope + push!(variable_init, :($actual_sym = $mangled_sym)) + end + end + ret = Expr(:let, Expr(:block), Expr(:block, match_logic, body)) + ret = CFGSpec(ret) + ret = init_cfg(ret) + esc(ret) end + +end # module end diff --git a/src/MatchImpl.jl b/src/MatchImpl.jl new file mode 100644 index 0000000..26465c4 --- /dev/null +++ b/src/MatchImpl.jl @@ -0,0 +1,487 @@ +module MatchImpl +export is_enum, pattern_uncall, pattern_unref, @switch, @match, Where, gen_match, gen_switch +export Q, unQ +import MLStyle +using MLStyle.Err +using MLStyle.MatchCore +using MLStyle.ExprTools + +using MLStyle.AbstractPattern +using MLStyle.AbstractPattern.BasicPatterns +OptionalLn = Union{LineNumberNode,Nothing} + +is_enum(_)::Bool = false +function pattern_uncall end +function pattern_unref end + +struct Where + value::Any + type::Any + type_parameters::AbstractArray{T,1} where {T} +end + +struct QuotePattern + value +end + +Base.@pure function qt2ex(ex::Any) + if ex isa Expr + Meta.isexpr(ex, :$) && return ex.args[1] + Expr(:call, Expr, QuoteNode(ex.head), (qt2ex(e) for e in ex.args if !(e isa LineNumberNode))...) + elseif ex isa QuoteNode + QuotePattern(qt2ex(ex.value)) + elseif ex isa Symbol + QuoteNode(ex) + else + ex + end +end + +function guess_type_from_expr(m::Module, ex::Any, tps::Set{Symbol}) + @sswitch ex begin + @case :($t{$(targs...)}) + # TODO: check if it is a type + return guess_type_from_expr(m, t, tps) + @case t::Type + return t + @case ::Symbol + ex in tps || isdefined(m, ex) && + #= TODO: check if it's a type =# + return getfield(m, ex) + return Any + @case _ + error("unrecognised type expression $ex") + end +end + +ex2tf(m::Module, a) = isprimitivetype(typeof(a)) ? literal(a) : error("invalid literal $a") +ex2tf(m::Module, l::LineNumberNode) = wildcard +ex2tf(m::Module, q::QuoteNode) = literal(q.value) +ex2tf(m::Module, s::String) = literal(s) +ex2tf(m::Module, n::Symbol) = + if n === :_ + wildcard + elseif n === :nothing + literal(nothing) + else + if isdefined(m, n) + p = getfield(m, n) + rec(x) = ex2tf(m, x) + is_enum(p) && return pattern_uncall(p, rec, [], [], []) + end + P_capture(n) + end + +_quote_extract(expr::Any, ::Int, ::Any, ::Any) = :($expr.value) + +function ex2tf(m::Module, s::QuotePattern) + p0 = P_type_of(QuoteNode) + p1 = decons(_quote_extract, [ex2tf(m, s.value)]) + and([p0, p1]) +end + +function ex2tf(m::Module, w::Where) + rec(x) = ex2tf(m, x) + @sswitch w begin + @case Where(; value = val, type = t, type_parameters = tps) + tp_set = get_type_parameters(tps)::Set{Symbol} + p_ty = guess_type_from_expr(m, t, tp_set) |> P_type_of + tp_vec = collect(tp_set) + sort!(tp_vec) + p_guard = guard() do target, scope, _ + + isempty(tp_vec) && return see_captured_vars(:($target isa $t), scope) + + tp_guard = foldr(tps, init=true) do tp, last + tp isa Symbol && return last + last === true && return tp + Expr(:&&, tp, last) + end + + tp_chk_ret = Expr(:tuple, tp_vec...) + if tp_guard !== true + tp_chk_ret = :($tp_guard ? $tp_chk_ret : nothing) + end + + targns = Symbol[] + fn = gensym("extract type params") + testn = gensym("test type params") + ty_accurate = gensym("accurate type param") + ret = Expr(:block) + suite = ret.args + for tp in tp_vec + targn = gensym(tp) + push!(targns, targn) + end + push!( + suite, + :($fn(::Type{$ty_accurate}) where {$(tp_vec...), $ty_accurate <: $t} = $tp_chk_ret), + :($fn(_) = nothing), + :($testn = $fn(typeof($target))), + Expr( + :if, + :($testn !== nothing), + Expr(:block, Expr(:(=), Expr(:tuple, targns...), testn), true), + false, + ), + ) + for i in eachindex(tp_vec) + scope[tp_vec[i]] = targns[i] + end + ret + end + return and([p_ty, p_guard, rec(val)]) + end +end + +function ex2tf(m::Module, ex::Expr) + eval = m.eval + rec(x) = ex2tf(m, x) + + @sswitch ex begin + @case Expr(:||, args) + return or(map(rec, args)) + @case Expr(:&&, args) + return and(map(rec, args)) + @case Expr(:if, [cond, Expr(:block, _)]) + return guard() do _, scope, _ + see_captured_vars(cond, scope) + end + @case Expr(:let, args) + bind = args[1] + @assert bind isa Expr + return if bind.head === :(=) + @assert bind.args[1] isa Symbol + P_bind(bind.args[1], bind.args[2], see_capture = true) + else + @assert bind.head === :block + binds = Function[ + P_bind(arg.args[1], arg.args[2], see_capture = true) for arg in bind.args + ] + push!(binds, wildcard) + and(binds) + end + @case Expr(:&, [expr]) + return guard() do target, scope, _ + see_captured_vars(:($target == $expr), scope) + end + @case Expr(:vect, elts) + tag, split = ellipsis_split(elts) + return tag isa Val{:vec} ? P_vector([rec(e) for e in split]) : + let (init, mid, tail) = split + P_vector3([rec(e) for e in init], rec(mid), [rec(e) for e in tail]) + end + @case Expr(:tuple, elts) + return P_tuple([rec(e) for e in elts]) + + @case Expr(:quote, [quoted]) + return rec(qt2ex(quoted)) + + @case Expr(:ref, [t, args...]) + t = eval(t) + return pattern_unref(t, rec, args) + + @case Expr( + :where, + [ + Expr(:call, [:($t{$(targs...)}), args...]) || + Expr(:call, [t, args...]) && let targs = [] + end, + tps..., + ], + ) && if t !== Where + end + t = eval(t) + return pattern_uncall(t, rec, tps, targs, args) + + @case ( + Expr(:call, [:($t{$(targs...)}), args...]) || + Expr(:call, [t, args...]) && let targs = [] + end + ) && if t !== Where + end + t = eval(t) + return pattern_uncall(t, rec, [], targs, args) + + @case Expr(:curly, [t, targs...]) + t = eval(t) + return pattern_uncall(t, rec, [], targs, []) + + @case :($val::$t where {$(tps...)}) || + :(::$t where {$(tps...)}) && let val = :_ + end || + :($val::$t) && let tps = [] + end || + :(::$t) && let val = :_, tps = [] + end + + return ex2tf(m, Where(val, t, tps)) + + @case :($ty[$pat for $reconstruct in $seq if $cond]) || + :($ty[$pat for $reconstruct in $seq]) && let cond = true end || + :[$pat for $reconstruct in $seq if $cond] && let ty = Any end || + :[$pat for $reconstruct in $seq] && let cond = true, ty = Any end && if seq isa Symbol end + + return uncomprehension(rec, ty, pat, reconstruct, seq, cond) + + @case a + error("unknown pattern syntax $(repr(a))") + end +end + +function uncomprehension(self::Function, ty::Any, pat::Any, reconstruct::Any, seq::Any, cond::Any) + eltype = guess_type_from_expr(self.m, ty, Set{Symbol}()) + p0 = P_type_of(AbstractArray{T, 1} where T <: eltype) + function extract(target::Any, ::Int, scope::ChainDict{Symbol, Symbol}, ln::LineNumberNode) + token = gensym("uncompreh token") + iter = gensym("uncompreh iter") + vec = gensym("uncompreh seq") + infer_flag = gensym("uncompreh flag") + fn = gensym("uncompreh func") + reconstruct_tmp = gensym("reconstruct") + mk_case(x) = Expr(:macrocall, Symbol("@case"), ln, x) + switch_body = quote + $(mk_case(pat)) + $reconstruct_tmp = $reconstruct + if $infer_flag isa $Val{true} + return $reconstruct_tmp + else + if $cond + push!($vec.value, $reconstruct_tmp) + end + return true + end + $(mk_case(:_)) + if $infer_flag isa $Val{true} + error("impossible") + else + return false + end + end + switch_stmt = Expr(:macrocall, GlobalRef(MLStyle, Symbol("@switch")), ln, iter, switch_body) + final = quote + $Base.@inline $fn($iter, $infer_flag::$Val) = $switch_stmt + $vec = $Base._return_type($fn, $Tuple{$Base.eltype($target), $Val{true}})[] + $vec = $Some($vec) + for $iter in $target + $token = $fn($iter, $Val(false)) + $token && continue + $vec = nothing + break + end + $vec + end + see_captured_vars(final, scope) + end + p1 = decons(extract, [self(Expr(:call, Some, seq))]) + return and([p0, p1]) +end + +const case_sym = Symbol("@case") + +macro switch(val, ex) + res = gen_switch(val, ex, __source__, __module__) + res = init_cfg(res) + esc(res) +end + +function gen_switch(val, ex, __source__::LineNumberNode, __module__::Module) + @assert Meta.isexpr(ex, :block) + clauses = Union{LineNumberNode,Pair{<:Function,Symbol}}[] + body = Expr(:block) + alphabeta = 'a':'z' + ln = __source__ + variable_init_blocks = Dict{Symbol, Expr}() + for i in eachindex(ex.args) + stmt = ex.args[i] + if Meta.isexpr(stmt, :macrocall) && + stmt.args[1] === case_sym && + length(stmt.args) == 3 + + pattern = try + ex2tf(__module__, stmt.args[3]) + catch e + e isa ErrorException && throw(PatternCompilationError(ln, e.msg)) + rethrow() + end + + k = length(variable_init_blocks) + 1 + br::Symbol = Symbol(string(alphabeta[k%26]), k) + push!(clauses, pattern => br) + + variable_init_block = Expr(:block) + variable_init_blocks[br] = variable_init_block + + push!(body.args, CFGLabel(br)) + push!(body.args, variable_init_block) + else + if stmt isa LineNumberNode + ln = stmt + push!(clauses, stmt) + end + push!(body.args, stmt) + end + end + + isempty(variable_init_blocks) && throw( + PatternCompilationError(__source__, "empty switch statements!") + ) + + terminal_scope, match_logic = backend(val, clauses, __source__) + for (br, branches_terminal_scope) in terminal_scope + variable_init = variable_init_blocks[br].args + for (actual_sym, mangled_sym) in branches_terminal_scope + push!(variable_init, :($actual_sym = $mangled_sym)) + end + end + CFGSpec(Expr(:block, match_logic, body)) +end + +Base.@pure function expr2tuple(expr) + :($expr.head, $expr.args) +end + +Base.@pure function packexpr(expr) + :([$expr.head, $expr.args...]) +end + +function pattern_uncall( + ::Type{Expr}, + self::Function, + type_params::AbstractArray, + type_args::AbstractArray, + args::AbstractArray, +) + isempty(type_params) || error("An Expr pattern requires no type params.") + isempty(type_args) || error("An Expr pattern requires no type arguments.") + @sswitch args begin + @case [Expr(:..., [_]), _...] + return and([P_type_of(Expr), P_slow_view(packexpr, self(Expr(:vect, args...)))]) + @case _ + end + + tcons(_...)::Type{Expr} = Expr + comp = PComp("Expr", tcons) + + p_tag = self(args[1]) + p_vec = self(Expr(:vect, view(args, 2:length(args))...)) + p_tuple = P_tuple([p_tag, p_vec]) + and([P_type_of(Expr), P_slow_view(expr2tuple, p_tuple)]) +end + +function pattern_uncall( + ::Type{Core.SimpleVector}, + self::Function, + type_params::AbstractArray, + type_args::AbstractArray, + args::AbstractArray, +) + isempty(type_params) || error("A svec pattern requires no type params.") + isempty(type_args) || error("A svec pattern requires no type arguments.") + + tag, split = ellipsis_split(args) + return tag isa Val{:vec} ? P_svec([self(e) for e in split]) : + let (init, mid, tail) = split + P_svec3([self(e) for e in init], self(mid), [self(e) for e in tail]) + end +end + +function pattern_uncall( + ::Type{QuoteNode}, + self::Function, + type_params::AbstractArray, + type_args::AbstractArray, + args::AbstractArray, +) + isempty(type_params) || error("A QuoteNode pattern requires no type params.") + isempty(type_args) || error("A QuoteNode pattern requires no type arguments.") + length(args) == 1 || error("A QuoteNode pattern accepts only 1 argument.") + self(QuotePattern(args[1])) +end + +function _some_guard1(expr::Any) + :($expr !== nothing) +end +function _some_tcons(t) + Some{T} where T <: t +end +const _some_comp = PComp("Some", _some_tcons; guard1=NoncachablePre(_some_guard1)) + +function pattern_uncall(::Type{Some}, self::Function, tparams::AbstractArray, targs::AbstractArray, args::AbstractArray) + isempty(tparams) || error("A (:) pattern requires no type params.") + isempty(targs) || error("A (:) pattern requires no type arguments.") + @assert length(args) === 1 + function some_extract(expr::Any, i::Int, ::Any, ::Any) + @assert i === 1 + :($expr.value) + end + decons(_some_comp, some_extract, [self(args[1])]) +end + +macro match(val, tbl) + res = gen_match(val, tbl, __source__, __module__) + res = init_cfg(res) + esc(res) +end + +function gen_match(val, tbl, __source__::LineNumberNode, __module__::Module) + @assert Meta.isexpr(tbl, :block) + clauses = Union{LineNumberNode,Pair{<:Function,Symbol}}[] + body = Expr(:block) + alphabeta = 'a':'z' + final_label = Symbol("FINAL") + final_res = gensym("final") + ln = __source__ + variable_init_blocks = Dict{Symbol, Expr}() + for i in eachindex(tbl.args) + ex = tbl.args[i] + @switch ex begin + @case :($a => $b) + pattern = try + ex2tf(__module__, a) + catch e + e isa ErrorException && throw(PatternCompilationError(ln, e.msg)) + rethrow() + end + k = length(variable_init_blocks) + 1 + br::Symbol = Symbol(string(alphabeta[k%26]), k) + push!(clauses, pattern => br) + + variable_init_block = Expr(:block) + return_expr = Expr(:block) + let_expr = Expr(:let, variable_init_block, b) + variable_init_blocks[br] = variable_init_block + + push!(body.args, CFGLabel(br)) + push!(body.args, :($final_res = $let_expr)) + push!(body.args, CFGJump(final_label)) + continue + @case ln::LineNumberNode + push!(clauses, ln) + push!(body.args, ln) + continue + # TODO: syntax error report + end + end + + isempty(variable_init_blocks) && throw( + PatternCompilationError( + __source__, + "empty match expression!" + ) + ) + + terminal_scope, match_logic = backend(val, clauses, __source__) + for (br, branches_terminal_scope) in terminal_scope + variable_init = variable_init_blocks[br].args + for (actual_sym, mangled_sym) in branches_terminal_scope + push!(variable_init, :($actual_sym = $mangled_sym)) + end + end + + push!(body.args, CFGLabel(final_label)) + push!(body.args, final_res) + + CFGSpec(Expr(:let, Expr(:block), Expr(:block, match_logic, body))) +end +end diff --git a/src/Modules/AST.Compat.jl b/src/Modules/AST.Compat.jl deleted file mode 100644 index c36ce1d..0000000 --- a/src/Modules/AST.Compat.jl +++ /dev/null @@ -1,66 +0,0 @@ -function capturing_analysis(expr, out, is_literal) - - @match expr begin - ::QuoteNode => - capturing_analysis(expr.value, out, true) - - if is_literal end && Expr(:$, args...) => - - foreach(x -> capturing_analysis(x, out, false), args) - - if is_literal end && Expr(_, args...) => - - foreach(x -> capturing_analysis(x, out, true), args) - - - if is_literal end && _ => nothing - - - # not literal - ::Symbol => (push!(out, expr); nothing) - - Expr(:quote, args...) => - foreach(x -> capturing_analysis(x, out, true), args) - - :(Do($(args...))) => - foreach(args) do arg - @match arg begin - Expr(:kw, key :: Symbol, value) => - begin - push!(out, key) - end - _ => nothing - end - end - :($a || $b) => - let out1 = Set(Symbol[]), - out2 = Set(Symbol[]) - - capturing_analysis(a, out1, false) - capturing_analysis(b, out2, false) - - union!(out, intersect(out1, out2)) - nothing - end - # type pattern - Expr(:(::), a, _) => capturing_analysis(a, out, false) - # dict pattern - :(Dict($(args...))) => - foreach(args) do arg - @match arg begin - :($_ => $v) => capturing_analysis(v, out, false) - _ => nothing - end - end - # app pattern - :($_($(args...))) => foreach(x -> capturing_analysis(x, out, false), args) - # other expr - Expr(_, args...) => foreach(x -> capturing_analysis(x, out, false), args) - # ref pattern - Expr(:&, _) || - # predicate - Expr(:function, _...) || - Expr(:if, _...) || - x => nothing - end -end \ No newline at end of file diff --git a/src/Modules/AST.jl b/src/Modules/AST.jl index a78a506..69bd5a6 100644 --- a/src/Modules/AST.jl +++ b/src/Modules/AST.jl @@ -1,28 +1,58 @@ module AST using MLStyle -using MLStyle.Render +using MLStyle.AbstractPattern using MLStyle.Err -include("AST.Compat.jl") -export @matchast, @capture +export @matchast, @capture, Capture -function matchast(target, actions, source, mod::Module) - stmts = @match actions begin - Expr(:quote, quote $(stmts...) end) => stmts +struct Capture end - _ => begin - msg = "Malformed ast template, the second arg should be a block with a series of pairs(`a => b`), at $(string(source))." - throw(SyntaxError(msg)) +function MLStyle.pattern_uncall( + ::Type{Capture}, + self::Function, + type_params::AbstractArray, + type_args::AbstractArray, + args::AbstractArray +) + isempty(type_params) || error("A Capture requires no type params.") + isempty(type_args) || error("A Capture pattern requires no type arguments.") + length(args) === 1 || error("A Capture pattern accepts one argument.") + function extract(::Any, ::Int, scope::ChainDict{Symbol, Symbol}, ::Any) + ret = Expr(:call, Dict) + for_chaindict(scope) do k, v + k = QuoteNode(k) + push!(ret.args, :($k => $v)) end + ret end - last_lnode = source - map(stmts) do stmt - @match stmt begin - ::LineNumberNode => (last_lnode = stmt) - :($a => $b) => :($(Expr(:quote, a)) => $b) - _ => throw(SyntaxError("Malformed ast template, should be formed as `a => b`, at $(string(last_lnode)).")) + decons(extract, [self(args[1])]) +end + +function matchast(target, actions, source::LineNumberNode, mod::Module) + @switch actions begin + @case Expr(:quote, Expr(:block, stmts...)) + last_lnode = source + cbl = Expr(:block) + for stmt in stmts + @switch stmt begin + @case ::LineNumberNode + last_lnode = stmt + continue + @case :($a => $b) + push!( + cbl.args, + last_lnode, + :($(Expr(:quote, a)) => $b) + ) + continue + @case _ + throw(SyntaxError("Malformed ast template, should be formed as `a => b`, at $(string(last_lnode)).")) + end end - end |> actions -> - gen_match(target, Expr(:block, actions...), source, mod) + return gen_match(target, cbl, source, mod) + @case _ + msg = "Malformed ast template, the second arg should be a block with a series of pairs(`a => b`), at $(string(source))." + throw(SyntaxError(msg)) + end end """ @@ -42,7 +72,9 @@ e.g., ``` """ macro matchast(template, actions) - matchast(template, actions, __source__, __module__) |> esc + res = matchast(template, actions, __source__, __module__) + res = init_cfg(res) + return esc(res) end """ @@ -62,32 +94,27 @@ If the template doesn't match input AST, return `nothing`. :(@capture) macro capture(template) - capture(template, __source__, __module__) |> esc + farg = gensym("expression") + fbody = + gen_capture(template, farg, __source__, __module__) |> init_cfg + fhead = Expr(:call, farg, farg) + esc(Expr(:function, fhead, fbody)) end macro capture(template, ex) - Expr(:call, capture(template, __source__, __module__), ex) |> esc + res = gen_capture(template, ex, __source__, __module__) + res = init_cfg(res) + return esc(res) end -function capture(template, source, mod::Module) - out_expr = @static VERSION < v"1.1.0" ? - begin - syms = Set(Symbol[]) - capturing_analysis(template, syms, true) - Expr(:call, Dict, (Expr(:call, =>, QuoteNode(each), each) for each in syms)...) - end : - :($Base.@locals) - - arg_sym = gensym() - let template = Expr(:quote, template), - actions = Expr(:block, :($template => $out_expr), :(_ => nothing)), - match_gen = gen_match(arg_sym, actions, source, mod) - @format [arg_sym, match_gen] quote - function (arg_sym) - match_gen - end - end - end +function gen_capture(template::Any, ex::Any, source::LineNumberNode, mod::Module) + template = Expr(:quote, template) + sym = :__SCOPE_CAPTURE__ + p_capture_scope = Expr(:call, Capture, sym) + p_whole = Expr(:&&, template, p_capture_scope) + tbl = Expr(:block, :($p_whole => $sym)) + + gen_match(ex, tbl, source, mod) end end diff --git a/src/Modules/Cond.jl b/src/Modules/Cond.jl index abce114..636f116 100644 --- a/src/Modules/Cond.jl +++ b/src/Modules/Cond.jl @@ -4,28 +4,27 @@ using MLStyle export @cond function cond(cases, source, mod::Module) - @match cases begin - quote - $(cases...) - end => - let default = Expr(:call, throw, "None of the branches have satisfied conditions, at $(string(source)).") - foldr(cases, init = default) do case, last - last_lnode = source - @match case begin - ::LineNumberNode => begin - last_lnode = case - Expr(:block, case, last) - end - :(_ => $b) => b - :($a => $b) => Expr(:if, a, b, last) - _ => throw("Invalid syntax for conditional branches at $last_lnode.") - end + @switch cases begin + @case Expr(:block, cases...) + default = Expr(:call, throw, "None of the branches have satisfied conditions, at $(string(source)).") + last_lnode = source + folded = foldr(cases, init=default) do case, last + @switch case begin + @case ::LineNumberNode + last_lnode = case + return last + @case :(_ => $b) + return b + @case :($a => $b) + return Expr(:if, a, b, Expr(:block, last_lnode, last)) + @case _ + throw("Invalid syntax for conditional branches at $last_lnode.") end end - _ => begin - msg = "Malformed ast template, the second arg should be a block with a series of pairs(`a => b`), at $(string(source))." - throw(SyntaxError(msg)) - end + return Expr(:block, source, folded) + @case _ + msg = "Malformed ast template, the second arg should be a block with a series of pairs(`a => b`), at $(string(source))." + throw(SyntaxError(msg)) end end diff --git a/src/Pervasives.jl b/src/Pervasives.jl index bdbad91..543498d 100644 --- a/src/Pervasives.jl +++ b/src/Pervasives.jl @@ -1,541 +1,148 @@ module Pervasives -using MLStyle.MatchCore -using MLStyle.Infras -using MLStyle.Extension -using MLStyle.Err -using MLStyle.Toolz: ($) - -import MLStyle.Infras: mk_gapp_pattern - -@use GADT - -function mk_pat_by(f) - (tag, case, mod) -> body -> - @format [f, tag, case, body] quote - f(tag, case) ? body : failed - end -end - -const strict_eq_types = Union{Int, Nothing} - -def_pattern(Pervasives, - predicate = x -> x isa strict_eq_types, - rewrite = mk_pat_by(===) -) - -def_pattern(Pervasives, - predicate = x -> x isa Union{Number, AbstractString, AbstractChar, QuoteNode}, - rewrite = (tag, case, mod) -> - let f = isimmutable(case) ? mk_pat_by(===) : mk_pat_by(isequal) - f(tag, case, mod) - end -) - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head == :(||), - rewrite = (tag, case, mod) -> - let pats = [mk_pattern(tag, arg, mod) for arg in case.args] - reduce(patternOr, pats) - end -) - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head == :(&&), - rewrite = (tag, case, mod) -> - let pats = [mk_pattern(tag, arg, mod) for arg in case.args] - reduce(∘, pats) - end -) - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head == :(&), - rewrite = (tag, case, mod) -> begin - @assert length(case.args) == 1 "invalid ref pattern." - var = case.args[1] - body -> @format [tag, var, body] quote - tag == var ? body : failed - end - end -) - -def_pattern(Pervasives, - predicate = x -> x isa Symbol && x == :(_), - rewrite = (_, _, _) -> identity -) - -def_pattern(Pervasives, - predicate = x -> x isa Symbol && x == :nothing, - rewrite = mk_pat_by(===) -) - -islower(s)::Bool = !isempty(s) && islowercase(s[1]) -isupper(s)::Bool = !isempty(s) && isuppercase(s[1]) - -def_pattern(Pervasives, - predicate = x -> x isa Symbol && islower ∘ string $ x, - rewrite = (tag, case, mod) -> body -> - @format [case, tag, body] quote - let case = tag - body - end - end -) - -# Not decided yet about capitalized symbol's semantics, for generic enum is impossible in Julia. -def_pattern(Pervasives, - predicate = x -> x isa Symbol && isupper ∘ string $ x, - rewrite = (tag, case, mod) -> - if used(:Enum, mod) - mk_app_pattern(tag, case, [], mod) - elseif used(:UppercaseCapturing, mod) - body -> - @format [case, tag, body] quote - let case = tag - body - end - end - else - @syntax_err "You should use extension `Enum` or `UppercaseCapturing` to specify the behaviour of $(string(case))." - end -) - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head === :curly, - rewrite = (tag, case, mod) -> mk_gapp_pattern(tag, [], case, [], mod) -) - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head == :if, - rewrite = (_, case, mod) -> - # TODO: perform syntax validation here. - let cond = case.args[1] - body -> @format [cond, body] quote - cond ? body : failed - end - end -) - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head == :function, - rewrite = (tag, case, mod) -> - let n = length(case.args) - if n === 1 - fn = case.args[1] - else - fn = case - end - body -> @format [body, fn, tag] quote - fn(tag) ? body : failed - end - end -) - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head === :tuple, - rewrite = (tag, case, mod) -> - let pat_elts = case.args, - n = length(pat_elts), - TARGET = mangle(mod), - IDENTS = [mangle(mod) for _ in 1:n] - map(1:n) do i - IDENT = IDENTS[i] - elt = pat_elts[i] - function (body) - Expr(:block, Expr(:(=), IDENT, :($TARGET[$i])), body) - end ∘ mk_pattern(IDENT, elt, mod) - end |> match_elts -> - let match_elts = reduce(∘, match_elts, init=identity) - (@typed_as NTuple{n, Any}) ∘ match_elts - end - end -) - - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head == :vect, - rewrite = (tag, case, mod) -> begin - elts = case.args - ordered_seq_match(tag, elts, mod) +using MLStyle +using MLStyle.AbstractPattern +using MLStyle.AbstractPattern +struct Many end +struct Do end +struct GuardBy end +export Many, Do, GuardBy + +function MLStyle.pattern_uncall(::typeof(:), self::Function, tparams::AbstractArray, targs::AbstractArray, args::AbstractArray) + isempty(tparams) || error("A (:) pattern requires no type params.") + isempty(targs) || error("A (:) pattern requires no type arguments.") + guard() do target, scope, _ + rng = Expr(:call, :, args...) + see_captured_vars(:($target in $rng), scope) end -) - -struct QuotePattern - value end -function mk_expr_template(expr :: Expr) - if expr.head == :($) - return expr.args[1] +function MLStyle.pattern_uncall(::Type{Dict}, self::Function, tparams::AbstractArray, targs::AbstractArray, args::AbstractArray) + isempty(tparams) || error("A (:) pattern requires no type params.") + isempty(targs) || error("A (:) pattern requires no type arguments.") + isempty(tparams) || return begin + call = Expr(:call, t, args...) + ann = Expr(:curly, t, targs...) + self(Where(call, ann, tparams)) end - rec = mk_expr_template - Expr(:call, :Expr, rec(expr.head), filter(x -> x !== _discard, map(rec, expr.args))...) -end - -struct Discard end -const _discard = Discard() - -function mk_expr_template(expr :: Symbol) - QuoteNode(expr) -end - -function mk_expr_template(expr :: QuoteNode) - QuotePattern(expr.value) -end - -function mk_expr_template(expr :: LineNumberNode) - _discard -end - -function mk_expr_template(expr) - expr -end - - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head == :quote, - rewrite = (tag, case, mod) -> begin - expr = case.args[1] - expr = mk_expr_template(expr) - mk_pattern(tag, expr, mod) - end -) - - -def_pattern(Pervasives, - predicate = x -> x isa QuotePattern, - rewrite = (tag, case, mod) -> begin - expr = case.value - expr = mk_expr_template(expr) - TARGET = mangle(mod) - VALUE = mangle(mod) - access_value(body) = - @format [body, TARGET, VALUE] quote - let VALUE = TARGET.value - body - end - end - (@typed_as QuoteNode) ∘ access_value ∘ mk_pattern(VALUE ,expr, mod) - end -) - - -def_app_pattern(Pervasives, - predicate = (hd_obj, args) -> hd_obj === (:), - rewrite = (tag, hd_obj, args, mod) -> begin - pat = Expr(:call, hd_obj, args...) - body -> @format [pat, tag, body] quote - tag in pat ? body : failed + pairs = Pair[] + for arg in args + @switch arg begin + @case :($a => $b) + push!(pairs, a => b) + continue + @case _ + error("A Dict pattern's sub-pattern should be the form of `(a::Symbol) => b`.") end end -) - -def_app_pattern(Pervasives, - predicate = (hd_obj, args) -> hd_obj === Expr && !isempty(args), - rewrite = (tag, hd_obj, args, mod) -> - let TARGET = mangle(mod) - length(args) === 1 ? - let arg = args[1] - (arg isa Expr && arg.head === :...) ? - let _ = (@assert length(arg.args) === 1), - lst = mangle(mod), - arg = arg.args[1], - perf_match = mk_pattern(lst, arg, mod) - exprargs_to_arr(body) = - @format [body, lst, TARGET] quote - lst = [TARGET.head, TARGET.args...] - body - end - (@typed_as Expr) ∘ exprargs_to_arr ∘ perf_match - end : - let HEAD = mangle(mod), - perf_match = mk_pattern(HEAD, arg, mod) - bind_head(body) = - @format [body, HEAD, TARGET] quote - !isempty(TARGET.args) ? failed : - let HEAD = TARGET.head - body - end - end - (@typed_as Expr) ∘ bind_head ∘ perf_match - end - end : - let HEAD = mangle(mod), - ARGS = mangle(mod) - - head_pat = args[1] - args_pat = args[2:end] - - assign_attrs(body) = - @format [body, HEAD, ARGS, TARGET] quote - let (HEAD, ARGS) = (TARGET.head, TARGET.args) - body - end - end - - (@typed_as Expr) ∘ assign_attrs ∘ mk_pattern(HEAD, head_pat, mod) ∘ ordered_seq_match(ARGS, args_pat, mod) - end + function dict_extract(expr::Any, i::Int, scope::ChainDict{Symbol, Symbol}, ::Any) + # cannot avoid performance overhead due to + # https://discourse.julialang.org/t/distinguish-dictionary-lookup-from-nothing-and-not-found/38654 + k, v = pairs[i] + if k isa Union{Expr, Symbol} + # how to reduce the generate code size? + # most of the cases, see_captured_vars is unnecessary. + k = see_captured_vars(k, scope) + end + :(haskey($expr, $k) ? Some($expr[$k]) : nothing) end -) - -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head == :(::), - rewrite = (tag, case, mod) -> - let args = (case.args..., ), - TARGET = mangle(mod) - function f(args :: NTuple{2, Any}) - pat, t = args - (@typed_as t) ∘ mk_pattern(TARGET, pat, mod) - end - - function f(args :: NTuple{1, Any}) - t = args[1] - @typed_as t - end - f(args) - end, - qualifiers = Set([internal]) -) - -def_app_pattern(Pervasives, - predicate = (hd_obj, args) -> hd_obj === Dict, - rewrite = (tag, hd_obj, args, mod) -> - let TARGET = mangle(mod) - - foldr(args, init=identity) do kv, last - if !(isa(kv, Expr) && kv.head === :call && (@eval mod $(kv.args[1])) === Pair) - throw $ - SyntaxError("Dictionary destruct must take patterns like Dict( => , ...)") - end - let (k, v) = kv.args[2:end], - IDENT = mangle(mod), - match_elt = mk_pattern(IDENT, v, mod) - function (body) - @format [IDENT, TARGET, get, k, body] quote - IDENT = get(TARGET, k) do - failed - end - IDENT === failed ? failed : body - end - end ∘ match_elt ∘ last - end - end |> match_kvs -> - (@typed_as Dict) ∘ match_kvs - end -) - -# arbitray ordered sequential patterns match -function ordered_seq_match(tag, elts, mod) - TARGET = mangle(mod) - NAME = mangle(mod) - T = mangle(mod) - function check_generic_array(body) - @format [AbstractArray, NAME, TARGET, body, T, tag] quote - - @inline __L__ function NAME(TARGET :: AbstractArray{T, 1}) where {T} - body - end - - @inline __L__ function NAME(_) - failed - end - - NAME(tag) - - end - end - length(elts) == 0 ? - ( - check_generic_array ∘ - function (body) - @format [isempty, body, tag] quote - isempty(tag) ? body : failed - end - end - ) : - begin - atleast_element_count = 0 - unpack_begin = nothing - unpack_end = 0 - unpack = [] - foreach(elts) do elt - if elt isa Expr && elt.head === :... - if unpack_begin === nothing - unpack_begin = atleast_element_count + 1 - else - throw $ - SyntaxError("Sequential unpacking can only be performed once at most.") - end - push!(unpack, elt.args[1]) - else - atleast_element_count = atleast_element_count + 1 - IDENT = mangle(mod) - index = unpack_begin === nothing ? - begin - :($TARGET[$atleast_element_count]) - end : - begin - let exp = :($TARGET[end - $unpack_end]) - unpack_end = unpack_end + 1 - exp - end - end - if elt === :_ - push!(unpack, identity) - else - perf_match = mk_pattern(IDENT, elt, mod) - push!( - unpack, - let IDENT = IDENT, index = index - function (body) - @format [IDENT, body, index] quote - IDENT = index - body - end - end ∘ perf_match - end - ) - end - end - end - if unpack_begin !== nothing - IDENT = mangle(mod) - check_len = body -> @format [body, TARGET, atleast_element_count, length] quote - length(TARGET) >= atleast_element_count ? body : failed - end - elt = unpack[unpack_begin] - if elt === :_ - unpack[unpack_begin] = identity - else - unpack[unpack_begin] = function (body) - @format [body, IDENT, TARGET, unpack_begin, unpack_end, length] quote - IDENT = view(TARGET, unpack_begin: (length(TARGET) - unpack_end)) - body - end - end ∘ mk_pattern(IDENT, elt, mod) - end - else - check_len = body -> @format [body, TARGET, length, atleast_element_count] quote - length(TARGET) === atleast_element_count ? body : failed - end - end - check_generic_array ∘ check_len ∘ foldr(∘, unpack) - - end + tchk = isempty(targs) ? P_type_of(Dict) : self(:(:: $Dict{$(targs...)})) + decomp = decons(dict_extract, [self(Expr(:call, Some, pair.second)) for pair in pairs]) + and([tchk, decomp]) end -struct _ManyToken end -struct _DoToken end - -export Many, Do -Many = _ManyToken() -Do = _DoToken() - - -function allow_assignment(expr :: Expr) - head = expr.head == :kw ? :(=) : expr.head - Expr(head, expr.args...) +function _allow_assignment!(expr :: Expr) + if expr.head === :kw || expr.head === :(=) + expr.head = :(=) + @assert expr.args[1] isa Symbol + end end -function allow_assignment(expr) - expr +function MLStyle.pattern_unref(::Type{Do}, self::Function, args::AbstractArray) + foreach(_allow_assignment!, args) + + effect() do target, scope, ln + ret = Expr(:block) + for arg in args + @switch arg begin + @case :($sym = $value) && if sym isa Symbol end + sym′ = get(scope, sym) do + nothing + end + bound = true + if sym′ === nothing + sym′ = gensym(sym) + bound = false + end + assignment = Expr(:(=), sym′ , see_captured_vars(value, scope)) + push!(ret.args, assignment) + if !bound + scope[sym] = sym′ + end + continue + @case _ + push!(ret.args, see_captured_vars(arg, scope)) + continue + end + end + ret + end end -def_app_pattern(Pervasives, - predicate = (hd_obj, args) -> hd_obj === Do, - rewrite = (_, _, args, _) -> - let action = Expr(:block, map(allow_assignment, args)...) - body -> @format [body, action] quote - (@inline __L__ function () - action - body - end)() - end - end -) - -def_app_pattern(Pervasives, - predicate = (hd_obj, args) -> hd_obj === QuoteNode && length(args) == 1, - rewrite = (tag, _, args, mod) -> - begin - VAR = mangle(mod) - - function (body) - @format [tag, VAR, body, failed, QuoteNode] quote - if tag isa QuoteNode - VAR = tag.value - body - else - failed - end - end - end ∘ - mk_pattern(VAR, args[1], mod) - - end -) - -def_app_pattern(Pervasives, - predicate = (hd_obj, args) -> hd_obj === Many, - rewrite = (tag, hd_obj, args, mod) -> - let inner = args[1], - ITER_VAR = mangle(mod), - TEST_VAR = mangle(mod), - iter_check = mk_pattern(ITER_VAR, inner, mod)(nothing) +function MLStyle.pattern_uncall(::Type{Do}, self::Function, tparams::AbstractArray, targs::AbstractArray, args::AbstractArray) + isempty(tparams) || error("A (:) pattern requires no type params.") + isempty(targs) || error("A (:) pattern requires no type arguments.") + MLStyle.pattern_unref(Do, self, args) +end - @assert length(args) === 1 "syntax form should be `Many(pat)`." - body -> @format [ITER_VAR, TEST_VAR, tag, iter_check, body] quote - TEST_VAR = true - for ITER_VAR in tag - if iter_check !== nothing - TEST_VAR = false - break - end - end - TEST_VAR ? body : failed - end +function MLStyle.pattern_uncall(::Type{GuardBy}, self::Function, tparams::AbstractArray, targs::AbstractArray, args::AbstractArray) + isempty(tparams) || error("A (:) pattern requires no type params.") + isempty(targs) || error("A (:) pattern requires no type arguments.") + @assert length(args) === 1 + guard() do target, _, _ + :($(args[1])($target)) end -) +end -def_pattern(Pervasives, - predicate = x -> x isa Expr && x.head === :where, - rewrite = (tag, case, mod) -> - begin - if !used(:GADT, mod) - @syntax_err "GADT extension hasn't been enabled. Try `@use GADT` and run your codes again." - end - # Not sure about if there's any other references of `where`, - # but GADT is particularly important, - # Tentatively, we use `where` for GADT support only. - @match case begin - :($hd($(tl...)) where {$(forall...)}) => mk_gapp_pattern(tag, forall, hd, tl, mod) - _ => - @syntax_err "Unknown usage of `where` in pattern region. Currently `where` is used for only GADT syntax." - end - end -) +function MLStyle.pattern_unref(::Type{Many}, self::Function, args::AbstractArray) + @assert length(args) === 1 + arg = args[1] + foreach(_allow_assignment!, args) + + let_pat = Expr(:let, Expr(:block, args...), Expr(:block)) + old = repr(Expr(:call, :Do, args...)) + new = repr(let_pat) + guard() do target, scope, ln + token = gensym("loop token") + iter = gensym("loop iter") + mk_case(x) = Expr(:macrocall, Symbol("@case"), ln, x) + switch_body = quote + $(mk_case(arg)) + continue + $(mk_case(:_)) + $token = false + break + end + switch_stmt = Expr(:macrocall, GlobalRef(MLStyle, Symbol("@switch")), ln, iter, switch_body) + final = quote + $token = true + for $iter in $target + $switch_stmt + end + $token + end + see_captured_vars(final, scope) + end +end -function mk_gapp_pattern(tag, forall, hd, tl, use_mod) - @match hd begin - ::Symbol && if isempty(forall) end => mk_app_pattern(tag, hd, tl, use_mod) - :($(ctor :: Symbol){$(spec_vars...)}) || ctor :: Symbol && Do(spec_vars = [])=> - begin - if isdefined(use_mod, ctor) - let ctor = getfield(use_mod, ctor) - for (def_mod, desc) in Infras.GAPP_DESTRUCTORS - if qualifier_test(desc.qualifiers, use_mod, def_mod) && desc.predicate(spec_vars, ctor, tl) - return desc.rewrite(tag, forall, spec_vars, ctor, tl, use_mod) - end - end - end - end - info = string(:($ctor{$(spec_vars...)}($(tl...)))) - throw(PatternUnsolvedException("invalid usage or unknown application case $info.")) - end - end +function MLStyle.pattern_uncall(::Type{Many}, self::Function, tparams::AbstractArray, targs::AbstractArray, args::AbstractArray) + isempty(tparams) || error("A (:) pattern requires no type params.") + isempty(targs) || error("A (:) pattern requires no type arguments.") + MLStyle.pattern_unref(Many, self, args) end +# QuoteNode, Do, Many end \ No newline at end of file diff --git a/src/Qualification.jl b/src/Qualification.jl index 1319154..08c54a6 100644 --- a/src/Qualification.jl +++ b/src/Qualification.jl @@ -1,14 +1,16 @@ module Qualification -using MLStyle.MatchCore -using MLStyle.Pervasives +export deprecate_qualifiers + +function deprecate_qualifiers(o) + s = string(o) + trunc = min(length(s), 20) + s = SubString(s, 1:trunc) + Base.depwarn( + "When using qualifier '$(s)': " * + "Scoping specifiers such as 'internal', 'public' are deprecated. " * + "Now the scope of a pattern is consistent with the visibility of the pattern object in current module.", + :qualifier + ) +end -export get_qualifier -get_qualifier(node, curmod) = - @match node begin - :public => invasive - :internal => internal - :(visible in [$(mods...)]) || - :(visible in $mod) && Do(mods = [mod]) => - share_with(Set(map(curmod.eval, mods))) - end end \ No newline at end of file diff --git a/src/Record.jl b/src/Record.jl index 9bd93a4..f2edea7 100644 --- a/src/Record.jl +++ b/src/Record.jl @@ -1,114 +1,124 @@ module Record using MLStyle -using MLStyle.Toolz: isCapitalized, ($), cons, nil using MLStyle.MatchCore +using MLStyle.MatchImpl +using MLStyle.AbstractPattern +using MLStyle.AbstractPattern.BasicPatterns using MLStyle.Qualification -using MLStyle.Infras -using MLStyle.Pervasives -using MLStyle.Render: render -export @as_record -export def_record +export @as_record, record_def -function def_record(ctor, record_fields, qualifier :: Qualifier, mod) - ctor_name = string(ctor) - n_destructor_args = length(record_fields) - mk_match(tag, hd_obj, destruct_fields, mod) = begin - check_if_given_field_names = map(destruct_fields) do field - @match field begin - Expr(:kw, _...) => true - _ => false - end - end - TARGET = mangle(mod) - if all(check_if_given_field_names) # begin if - map(destruct_fields) do field_ - @match field_ begin - Expr(:kw, field::Symbol, pat) => begin - let ident = mangle(mod), field = field - function(body) - @format [TARGET, body, ident] quote - ident = TARGET.$field - body - end - end ∘ mk_pattern(ident, pat, mod) - end - end - _ => @syntax_err "The field name of destructor must be a Symbol!" +function P_partial_struct_decons(t, partial_fields, ps, prepr::AbstractString="$t") + function tcons(_...) + t + end + + comp = PComp( + prepr, tcons; + ) + function extract(sub::Any, i::Int, ::Any, ::Any) + :($sub.$(partial_fields[i])) + end + decons(comp, extract, ps) +end + +function record_def(Struct, line::LineNumberNode, ::Module) + quote + $line + function $MatchImpl.pattern_uncall(t::Type{$Struct}, self::Function, type_params, type_args, args) + $line + isempty(type_params) || return begin + call = Expr(:call, t, args...) + ann = Expr(:curly, t, type_args...) + self(Where(call, ann, type_params)) + end + all_field_names = fieldnames(t) + partial_field_names = Symbol[] + patterns = Function[] + $MatchImpl.@switch args begin + @case [Expr(:parameters, kwargs...), args...] + @goto endswitch + @case let kwargs = [] end + @goto endswitch end + @label endswitch + n_args = length(args) + if all(Meta.isexpr(arg, :kw) for arg in args) + for arg in args + field_name = arg.args[1] + field_name in all_field_names || error("$t has no field $field_name.") + push!(partial_field_names, field_name) + push!(patterns, self(arg.args[2])) + end + elseif length(all_field_names) === n_args + append!(patterns, map(self, args)) + append!(partial_field_names, all_field_names) + elseif n_args === 1 && args[1] === :_ + elseif n_args !== 0 + error("count of positional fields should be 0 or the same as the fields($all_field_names)") end - elseif all(map(!, check_if_given_field_names)) - n_d = length(destruct_fields) - if n_d == 1 && destruct_fields[1] == :(_) - [] - # ignore fields - else - @assert n_d == n_destructor_args "Malformed destructing for case class $ctor_name(from module $(nameof(mod)))." - map(zip(destruct_fields, record_fields)) do (pat, field) - let ident = mangle(mod) - function (body) - @format [TARGET, body, ident] quote - ident = TARGET.$field - body - end - end ∘ mk_pattern(ident, pat, mod) - end + for e in kwargs + $MatchImpl.@switch e begin + @case :: Symbol + e in all_field_names || error("unknown field name $e for $t when field punnning.") + push!(partial_field_names, e) + push!(patterns, $P_capture(e)) + continue + @case Expr(:kw, key::Symbol, value) + key in all_field_names || error("unknown field name $key for $t when field punnning.") + push!(partial_field_names, key) + push!(patterns, $and([$P_capture(key), self(value)])) + continue + @case _ + + error("unknown sub-pattern $e in " * string(t) * ".") end end - else - @syntax_err "Destructor should be used in the form of `C(a, b, c)` or `C(a=a, b=b, c=c)` or `C(_)`" - end |> x -> (TARGET, reduce(∘, x, init=identity)) + + ret = $P_partial_struct_decons(t, partial_field_names, patterns) + isempty(type_args) && return ret + $and([self(Expr(:(::), Expr(:curly, t, type_args...))) , ret]) + end + end +end +function as_record(n, line::LineNumberNode, __module__::Module) + @switch n begin + @case ::Symbol + return record_def(n, line, __module__) + @case :(struct $hd{$(_...)} + $(_...) + end) || + :(struct $hd{$(_...)} <: $_ + $(_...) + end) || + :(struct $hd <: $_ + $(_...) + end) || + :(struct $hd + $(_...) + end) + return Expr( + :block, + n, + record_def(hd, line) + ) + @case _ + error("malformed structure $n") end +end - def_app_pattern(mod, - predicate = (hd_obj, args) -> hd_obj === ctor, - rewrite = (tag, hd_obj, destruct_fields, mod) -> begin - TARGET, match_fields = mk_match(tag, hd_obj, destruct_fields, mod) - (@typed_as hd_obj) ∘ match_fields - end, - qualifiers = Set([qualifier])) - # GADT syntax support!!! - def_gapp_pattern(mod, - predicate = (spec_vars, hd_obj, args) -> hd_obj === ctor, - rewrite = (tag, forall, spec_vars, hd_obj, destruct_fields, mod) -> begin - hd = :($hd_obj{$(spec_vars...)}) - TARGET, match_fields = mk_match(tag, hd, destruct_fields, mod) - if isempty(forall) - @typed_as hd - else - function (body) - NAME = mangle(mod) - @format [TARGET, tag, body, hd] quote - @inline __L__ function NAME(TARGET :: hd) where {$(forall...)} - body - end - @inline L function NAME(_) - failed - end - NAME(tag) - end - end - end ∘ match_fields - end, - qualifiers = Set([qualifier])) - ctor +macro as_record(qualifier, n) + deprecate_qualifiers(qualifier) + esc(as_record(n, __source__, __module__)) end - -macro as_record(qualifier, ctor) - let mod = __module__, ctor = mod.eval(ctor) - def_record(ctor, fieldnames(ctor), get_qualifier(qualifier, mod), mod) - end +macro as_record(n) + esc(as_record(n, __source__, __module__)) end -macro as_record(ctor) - let mod = __module__, ctor = mod.eval(ctor) - def_record(ctor, fieldnames(ctor), get_qualifier(:public, mod), mod) - end end -end \ No newline at end of file diff --git a/src/Render.jl b/src/Render.jl index dd1052e..859cd92 100644 --- a/src/Render.jl +++ b/src/Render.jl @@ -1,92 +1,80 @@ -module Render -using Base: get -export render, @format, format, rmlines - -struct Discard end -const discard = Discard() - -function render(expr, pair :: Pair{Symbol, Any}) - render(expr, Dict(pair)) -end - -# function rmlines(expr::Expr) -# let args = filter(map(rmlines, expr.args)) do arg -# arg !== discard -# end -# Expr(expr.head, args...) -# end -# end - -# function rmlines(::LineNumberNode) -# discard -# end - -# function rmlines(a) -# a -# end - -function render(expr, config :: Dict{Symbol, Any}) - function visit(expr :: Expr) - hd = expr.head - tl = filter(x -> x !== discard, map(visit, expr.args)) - Expr(hd, tl...) - end - - function visit(sym :: Symbol) - get(config, sym) do - sym - end - end - - function visit(:: LineNumberNode) - discard - end - - function visit(a) - a - end - - visit(expr) -end - -function format(args, template) - function dispatch(arg :: Symbol) - Expr(:call, :(=>), QuoteNode(arg), arg) - end - - function dispatch(arg :: Pair) - Expr(:call, :(=>), QuoteNode(arg[1]), arg[2]) - end - - function dispatch(arg :: Expr) - @assert arg.head == :(=) - sym = arg.args[1] - @assert sym isa Symbol "$sym" - value = arg.args[2] - Expr(:call, :(=>), QuoteNode(sym), value) - end - function dispatch(_) - throw("Unknown argtype") - end - - constlist = map(dispatch, args.args) - constlist = Expr(:vect, constlist...) - config = Expr(:call, Dict{Symbol, Any}, constlist) - - wrap = @static VERSION < v"1.1.0" ? (x -> x) : (x -> Expr(:call, merge, :(Base.@locals), x)) - Expr(:call, render, template, wrap(config)) -end - -function format(template) - Expr(:call, render, template, :(Base.@locals)) -end - -macro format(args, template) - esc(format(args, template)) -end - -macro format(template) - esc(format(template)) -end - -end +module Render +using Base: get +export render, @format, format + +struct Discard end +const discard = Discard() + +function render(expr, pair :: Pair{Symbol, Any}) + render(expr, Dict(pair)) +end + +function render(expr, config :: Dict{Symbol, Any}) + function visit(expr :: Expr) + hd = expr.head + tl = filter(x -> x !== discard, map(visit, expr.args)) + Expr(hd, tl...) + end + + function visit(sym :: Symbol) + get(config, sym) do + sym + end + end + + function visit(:: LineNumberNode) + discard + end + + function visit(a) + a + end + + visit(expr) +end + +function format(args, template) + function dispatch(arg :: Symbol) + Expr(:call, :(=>), QuoteNode(arg), arg) + end + + function dispatch(arg :: Pair) + Expr(:call, :(=>), QuoteNode(arg[1]), arg[2]) + end + + function dispatch(arg :: Expr) + @assert arg.head == :(=) + sym = arg.args[1] + @assert sym isa Symbol "$sym" + value = arg.args[2] + Expr(:call, :(=>), QuoteNode(sym), value) + end + function dispatch(_) + throw("Unknown argtype") + end + + constlist = map(dispatch, args.args) + constlist = Expr(:vect, constlist...) + config = Expr(:call, Dict{Symbol, Any}, constlist) + + wrap = @static VERSION < v"1.1.0" ? (x -> x) : (x -> Expr(:call, merge, :(Base.@locals), x)) + Expr(:call, render, template, wrap(config)) +end + +function format(template) + Expr(:call, render, template, :(Base.@locals)) +end + +macro format(args, template) + esc(format(args, template)) +end + +macro format(template) + esc(format(template)) +end + +for deprecated_sym in [:format, :render, Symbol("@format")] + @eval Base.@deprecate_moved $deprecated_sym "ASTRender.jl" +end + +end \ No newline at end of file diff --git a/src/StandardPatterns/Active.jl b/src/StandardPatterns/Active.jl index 965355f..4ab5386 100644 --- a/src/StandardPatterns/Active.jl +++ b/src/StandardPatterns/Active.jl @@ -1,79 +1,89 @@ # Active patterns module Active using MLStyle -using MLStyle.Infras -using MLStyle.MatchCore using MLStyle.Qualification -using MLStyle.TypeVarExtraction +using MLStyle.AbstractPattern -export @active, def_active_pattern +export @active, active_def -function def_active_pattern(qualifier_ast, case, active_body, mod) - (case_name, IDENTS, param) = @match case begin - :($(case_name :: Symbol)($param)) => (case_name, nothing, param) - :($(case_name :: Symbol){$(idents...)}($param)) => (case_name, idents, param) - end - TARGET = mangle(mod) +function active_def(P, body, mod::Module, line::LineNumberNode) - if !isdefined(mod, case_name) - mod.eval(quote struct $case_name end end) + @switch P begin + @case Expr(:call, + Expr(:curly, t, type_args...) || t && let type_args = [] end, + arg + ) && if t isa Symbol end + @goto endwitch + @case _ + error("malformed active pattern definition: $P") end - qualifier = get_qualifier(qualifier_ast, mod) - case_obj = getfield(mod, case_name) - if IDENTS === nothing - def_app_pattern(mod, - predicate = (hd_obj, args) -> hd_obj === case_obj, - rewrite = (tag, hd_obj, args, mod) -> begin - (test_var, pat) = @match length(args) begin - 0 => (false, true) - 1 => (nothing, args[1]) - _ => (nothing, Expr(:tuple, args...)) - end - - function (body) - @format [tag, test_var, param, TARGET, active_body, body] quote - let TARGET = - let param = tag - active_body - end - TARGET === test_var ? failed : body - end - end - end ∘ mk_pattern(TARGET, pat, mod) - end, - qualifiers = Set([qualifier])) + @label endwitch + + definition = if isdefined(mod, t) + line else - n_idents = length(IDENTS) - def_gapp_pattern(mod, - predicate = (spec_vars, hd_obj, args) -> hd_obj === case_obj && length(spec_vars) === n_idents, - rewrite = (tag, forall, spec_vars, hd_obj, args, mod) -> begin - (test_var, pat) = @match length(args) begin - 0 => (false, true) - 1 => (nothing, args[1]) - _ => (nothing, Expr(:tuple, args...)) - end - assign_elts_and_active_body = - let arr = [:($IDENT = $(spec_vars[i])) - for (i, IDENT) - in enumerate(IDENTS)] - Expr(:let, Expr(:block, arr...), Expr(:block, active_body)) - end - function (body) - @format [tag, test_var, param, TARGET, assign_elts_and_active_body, body] quote - let TARGET = - let param = tag - assign_elts_and_active_body - end - TARGET === test_var ? failed : body - end - end - end ∘ mk_pattern(TARGET, pat, mod) - end, - qualifiers = Set([qualifier])) + :(struct $t end) + end + parametric = isempty(type_args) ? [] : type_args + prepr = "$P" + token = gensym(prepr) + v_ty = Val{(view, token)} + v_val = Val((view, token)) + + quote + $definition + (::$v_ty)($(parametric...), ) = $arg -> $body + $line + function $MatchImpl.pattern_uncall(t::($t isa Function ? typeof($t) : Type{$t}), self::Function, type_params, type_args, args) + $line + isempty(type_params) || error("A ($t) pattern requires no type params.") + parametric = isempty(type_args) ? [] : type_args + n_args = length(args) + + function trans(expr) + Expr(:call, Expr(:call, $v_val, parametric...), expr) + end + + function guard2(expr) + if n_args === 0 + :($expr isa Bool && $expr) + elseif n_args === 1 + :($expr isa Some && $expr !== nothing) + else + :($expr isa $Tuple && length($expr) === $n_args) + end + end + + extract = if n_args <= 1 + function (expr::Any, i::Int, ::Any, ::Any) + expr + end + else + function (expr::Any, i::Int, ::Any, ::Any) + :($expr[$i]) + end + end + + type_infer(_...) = Any + + comp = $PComp( + $prepr, type_infer; + view=$SimpleCachablePre(trans), + guard2=$NoncachablePre(guard2) + ) + ps = if n_args === 0 + [] + elseif n_args === 1 + [self(Expr(:call, Some, args[1]))] + else + [self(e) for e in args] + end + $decons(comp, extract, ps) + end end - nothing end + """ Simple active pattern implementation. You can give a qualifier in the first argument of `@active` to customize its visibility in other modules. @@ -102,11 +112,12 @@ You can give a qualifier in the first argument of `@active` to customize its vis ``` """ macro active(qualifier, case, active_body) - def_active_pattern(qualifier, case, active_body, __module__) + deprecate_qualifiers(qualifier) + active_def(case, active_body, __module__, __source__) |> esc end macro active(case, active_body) - def_active_pattern(:public, case, active_body, __module__) + active_def(case, active_body, __module__, __source__) |> esc end end \ No newline at end of file diff --git a/src/StandardPatterns/LambdaCases.jl b/src/StandardPatterns/LambdaCases.jl index 7eb4db3..2de15c3 100644 --- a/src/StandardPatterns/LambdaCases.jl +++ b/src/StandardPatterns/LambdaCases.jl @@ -1,6 +1,7 @@ module LambdaCases using MLStyle -using MLStyle.Render +using MLStyle.Sugars +using MLStyle.AbstractPattern: init_cfg export gen_lambda, @λ @@ -17,43 +18,33 @@ function gen_lambda(cases, source :: LineNumberNode, mod :: Module) :($case => $block) end end - @match cases begin - :($a => $b) && Do(bs = [b]) || - :($a -> $(bs...)) => - let pair = make_pair_expr(a, bs), - cbl = Expr(:block, source, pair), - match_expr = gen_match(TARGET, cbl, source, mod) - - @format [TARGET, source, match_expr] quote - source - function (TARGET) - match_expr - end - end - end - - Do(stmts=[]) && - quote - $(Many( - (a :: LineNumberNode) && Do(push!(stmts , a)) || - (:($a => $b) && Do(bs=[b]) || :($a -> $(bs...))) && - Do(push!(stmts, make_pair_expr(a, bs))) - )...) - end => - let cbl = Expr(:block, source, stmts...), - match_expr = gen_match(TARGET, cbl, source, mod) - - @format [source, match_expr, TARGET] quote - source - function (TARGET) - match_expr - end - end - end - - _ => @syntax_err "Syntax error in lambda case definition!" + @switch cases begin + @case :($a -> $(bs...)) || + :($a => $b) && let bs = [b] end + + pair = make_pair_expr(a, bs) + cbl = Expr(:block, source, pair) + match_expr = gen_match(TARGET, cbl, source, mod) + @goto AA + + @case let stmts=[] end && Expr(:block, + Many[ + a :: LineNumberNode && Do[push!(stmts, a)] || + Or[:($a => $b) && let bs=[b] end, :($a -> $(bs...))] && + Do[push!(stmts, make_pair_expr(a, bs))] + ]... + ) + + cbl = Expr(:block, source, stmts...) + match_expr = gen_match(TARGET, cbl, source, mod) + @goto AA end + @label AA + Expr(:function, + Expr(:call, TARGET, TARGET), + Expr(:block, source, init_cfg(match_expr)) + ) end """ diff --git a/src/StandardPatterns/TypeVarDecons.jl b/src/StandardPatterns/TypeVarDecons.jl deleted file mode 100644 index bfdb942..0000000 --- a/src/StandardPatterns/TypeVarDecons.jl +++ /dev/null @@ -1,68 +0,0 @@ -# Type variable deconstruction patterns -module TypeVarDecons -using MLStyle -using MLStyle.Infras -using MLStyle.MatchCore -using MLStyle.Qualification -using MLStyle.TypeVarExtraction - -export type_matching - -macro type_matching(t, forall) - quote - NAME = mangle(mod) - __T__ = $t - __FORALL__ = $forall - if !($any_constraint(__T__, __FORALL__)) - function (body) - @format [body, tag, NAME, TARGET, __T__] quote - @inline __L__ function NAME(TARGET :: __T__) where {$(__FORALL__...)} - __T__ # if not put this here, an error would be raised : "local variable XXX cannot be used in closure declaration" - body - end - NAME(tag) - end - end - else - function (body) - @format [body, tag, NAME, TARGET, __T__] quote - @inline __L__ function NAME(TARGET :: __T__) where {$(__FORALL__...)} - __T__ - body - end - @inline __L__ function NAME(_) - failed - end - NAME(tag) - end - end - end - end |> esc -end - -def_pattern(TypeVarDecons, - predicate = x -> x isa Expr && x.head == :(::), - rewrite = (tag, case, mod) -> - let args = (case.args..., ), - TARGET = mangle(mod) - function for_type(t) - @match t begin - :($typ where {$(tvars...)}) => (@type_matching typ tvars) - _ => @typed_as t - - end - end - - function f(args :: NTuple{2, Any}) - pat, t = args - for_type(t) ∘ mk_pattern(TARGET, pat, mod) - end - - function f(args :: NTuple{1, Any}) - t = args[1] - for_type(t) - end - f(args) - end) - -end \ No newline at end of file diff --git a/src/StandardPatterns/Uncomprehensions.jl b/src/StandardPatterns/Uncomprehensions.jl deleted file mode 100644 index 4b80097..0000000 --- a/src/StandardPatterns/Uncomprehensions.jl +++ /dev/null @@ -1,101 +0,0 @@ -module Uncomprehensions - -using MLStyle -using MLStyle.MatchCore: mangle -using MLStyle.Infras: @format - -def_pattern(Uncomprehensions, - predicate = (x -> @match x begin - :[$_ for $_ in $_ if $_] || - :[$_ for $_ in $_] => true - _ => false - end), - rewrite = (tag, case, mod) -> begin - @match case begin - :[$expr for $iter in $seq if $cond] || - :[$expr for $iter in $seq] && Do(cond=nothing) => begin - function (body) - iter_var = mangle(mod) - - # the name of element that'll be pushed to the result vector - produced_elt_var = mangle(mod) - - inner_test_var = mangle(mod) - seq_var = mangle(mod) - - - # pattern is `seq`, return expr is `body` - decons_seq = mk_pattern(seq_var, seq, mod)(body) - lnode = LineNumberNode(1, "Uncomprehensions.jl") - if cond === nothing - # pattern is `expr`, return expr is `iter` - decons_elt = mk_pattern(iter_var, expr, mod)(iter) - @format [ - seq_var, iter_var, inner_test_var, produced_elt_var, - body, tag, decons_elt, decons_seq, push!, lnode - ] quote - if tag isa Vector - let seq_var = [], inner_test_var = true - - for iter_var in tag - produced_elt_var = decons_elt - if produced_elt_var === failed - inner_test_var = false - break - end - push!(seq_var, produced_elt_var) - end - - if inner_test_var - decons_seq - else - failed - end - end - else - failed - end - end - else - mid_var = mangle(mod) - decons_elt = mk_pattern(iter_var, expr, mod)( - @format [cond, iter, mid_var] quote - mid_var = iter - mid_var === failed ? failed : (cond, mid_var) - end - ) - @format [ - seq_var, iter_var, inner_test_var, produced_elt_var, - body, tag, decons_elt, decons_seq, push!, lnode - ] quote - if tag isa Vector - let seq_var = [], inner_test_var = true - - for iter_var in tag - produced_elt_var = decons_elt - if produced_elt_var === failed - inner_test_var = false - break - end - if produced_elt_var[1] - push!(seq_var, produced_elt_var[2]) - end - end - - if inner_test_var - decons_seq - else - failed - end - end - else - failed - end - end - end - end - end - end - end -) -end \ No newline at end of file diff --git a/src/StandardPatterns/WhenCases.jl b/src/StandardPatterns/WhenCases.jl index 3c7bc43..88fdbaf 100644 --- a/src/StandardPatterns/WhenCases.jl +++ b/src/StandardPatterns/WhenCases.jl @@ -1,6 +1,7 @@ module WhenCases using MLStyle -using MLStyle.Render +using MLStyle.Sugars: Q +using MLStyle.AbstractPattern: init_cfg export @when, @otherwise, gen_when @@ -16,9 +17,7 @@ function split_case_and_block(stmts, first_bindings, first_source) take_size = block_size for i in block_size:-1:1 take_size = i - if !(current_block[i] isa LineNumberNode) - break - end + current_block[i] isa LineNumberNode || break end # push current_block(cache) to blocks, then clear cache push!(blocks, Expr(:block, view(current_block, 1:take_size)...)) @@ -32,23 +31,25 @@ function split_case_and_block(stmts, first_bindings, first_source) end for stmt in stmts - @match stmt begin - :(@when $source begin $(bindings...) end) || - :(@when $source $elt) && Do(bindings=[elt]) => - begin - push!(binding_seqs, bindings) - push!(sources, source) - make_block!() - end - :(@otherwise $source) => - begin - push!(binding_seqs, []) - push!(sources, source) - make_block!() - end - a => append_block!(a) + @switch stmt begin + @case :(@when $source begin $(bindings...) end) || + Q[@when $source $elt] && let bindings=[elt] end + + push!(binding_seqs, bindings) + push!(sources, source) + make_block!() + continue + @case Q[@otherwise $source] + push!(binding_seqs, []) + push!(sources, source) + make_block!() + continue + @case a + append_block!(a) + continue end end + make_block!() # thus, the length of `sources`, `binding_seqs` and `blocks` # are guaranteed to be the same. @@ -77,54 +78,39 @@ Code generation for `@when`. You should pass an `Expr(:let, ...)` as the first argument. """ function gen_when(let_expr, source :: LineNumberNode, mod :: Module) - @match let_expr begin - Expr(:let, - Expr(:block, bindings...) || a && Do(bindings = [a]), - Expr(:block, stmts...) || b && Do(stmts = [b])) => - - begin - sources_cases_blocks = split_case_and_block(stmts, bindings, source) - foldr(sources_cases_blocks, init=:nothing) do (source, bindings, block), last_block - foldr(bindings, init=block) do each, last_ret - @match each begin - :($a = $b) => - let cbl = @format [source, a, last_ret, last_block] quote - source - a => last_ret - _ => last_block - end - gen_match(b, cbl, source, mod) - # :(@match $b $cbl) - end - - new_source::LineNumberNode => begin - source = new_source - last_ret - end - - # match `cond.?` or `if cond end` - :(if $a; $(_...) end) || - :($a.?) => @format [source, a, last_ret, last_block] quote - source - a ? last_ret : last_block - end - - # match something like: `let a; a = 1; a end` - a => @format [source, a, last_ret] quote - source - let a - last_ret - end - end - end + @switch let_expr begin + @case Expr(:let, + Expr(:block, bindings...) || a && let bindings = [a] end, + Expr(:block, stmts...) || b && let stmts = [b] end) + sources_cases_blocks = split_case_and_block(stmts, bindings, source) + return foldr(sources_cases_blocks, init=:nothing) do (source, bindings, block), last_block + foldr(bindings, init=block) do each, last_ret + @switch each begin + @case :($a = $b) + cbl = Expr(:block, source, :($a => $last_ret), :(_ => $last_block)) + return gen_match(b, cbl, source, mod) + + @case new_source::LineNumberNode + source = new_source + return last_ret + + # match `cond.?` or `if cond end` + @case :(if $a; $(_...) end) || + :($a.?) + + return Expr(:block, source, :($a ? $last_ret : $last_block)) + + # match something like: `let a; a = 1; a end` + @case a + return Expr(:block, source, Expr(:let, a, last_ret)) end end end - a => let s = string(a), - short_msg = SubString(s, 1, min(length(s), 20)) - throw(SyntaxError("Expected a let expression, got a `$short_msg` at $(string(source)).")) - end + @case a + s = string(a) + short_msg = SubString(s, 1, min(length(s), 20)) + throw(SyntaxError("Expected a let expression, got a `$short_msg` at $(string(source)).")) end end @@ -193,16 +179,20 @@ end ``` """ macro when(let_expr) - gen_when(let_expr, __source__, __module__) |> esc + res = gen_when(let_expr, __source__, __module__) + res = init_cfg(res) + esc(res) end macro when(assignment, ret) @match assignment begin :($_ = $_) => let let_expr = Expr(:let, Expr(:block, assignment), ret) - gen_when(let_expr, __source__, __module__) |> esc + res = gen_when(let_expr, __source__, __module__) + res = init_cfg(res) + esc(res) end - _ => @syntax_err "Not match the form of `@when a = b expr`" + _ => throw(SyntaxError("Not match the form of `@when a = b expr`")) end end diff --git a/src/Sugars.jl b/src/Sugars.jl new file mode 100644 index 0000000..ca028b0 --- /dev/null +++ b/src/Sugars.jl @@ -0,0 +1,38 @@ +module Sugars +using MLStyle +using MLStyle.AbstractPattern + +export Q, And, Or + +struct Q end +struct And end +struct Or end + +function MLStyle.pattern_unref( + ::Type{Or}, + self::Function, + args::AbstractArray +) + isempty(args) && error("An Or pattern should take more than 1 clause!") + or([self(arg) for arg in args]) +end + +function MLStyle.pattern_unref( + ::Type{And}, + self::Function, + args::AbstractArray +) + isempty(args) && error("An And pattern should take more than 1 clause!") + and([self(arg) for arg in args]) +end + +function MLStyle.pattern_unref( + ::Type{Q}, + self::Function, + args::AbstractArray +) + length(args) === 1 || error("A Q pattern should take only 1 argument!") + self(Expr(:quote, args[1])) +end + +end \ No newline at end of file diff --git a/src/TypeVarExtraction.jl b/src/TypeVarExtraction.jl deleted file mode 100644 index 50c0fc7..0000000 --- a/src/TypeVarExtraction.jl +++ /dev/null @@ -1,61 +0,0 @@ -module TypeVarExtraction - -using MLStyle -using MLStyle.Toolz.List: cons, nil -using MLStyle.Infras -using MLStyle.MatchCore -using MLStyle.Qualification - -export TypeVar, Relation, ChainRelation -export any_constraint, to_tvar, extract_tvars - -struct TypeVar - t :: Symbol -end - -struct Relation - l :: Symbol - op :: Symbol - r -end - -struct ChainRelation - var :: Symbol - lower - super -end - -function any_constraint(t, forall) - - function is_rel(::Relation) - true - end - - function is_rel(::ChainRelation) - true - end - - function is_rel(::TypeVar) - false - end - - !(t isa Symbol) || any(is_rel, collect(extract_tvars(forall))) -end - -function extract_tvars(t :: AbstractArray) - @match t begin - [] => nil() - [hd && if hd isa Symbol end, tl...] => cons(TypeVar(hd), extract_tvars(tl)) - [:($hd <: $r), tl...] => cons(Relation(hd, :<:, r), extract_tvars(tl)) - [:($hd >: $(r)), tl...] => cons(Relation(hd, Symbol(">:"), r), extract_tvars(tl)) - [:($lower <: $hd <: $super), tl...] || - [:($super >: $hd >: $lower), tl...] => cons(ChainRelation(hd, lower, super)) - _ => @syntax_err "invalid tvars($t)" - end -end - -to_tvar(t::TypeVar) = t.t -to_tvar(t::Relation) = t.l -to_tvar(t::ChainRelation) = t.var - -end \ No newline at end of file diff --git a/test/MQuery/MQuery.MacroProcessor.jl b/test/MQuery/MQuery.MacroProcessor.jl index df0ec45..38f2fb8 100644 --- a/test/MQuery/MQuery.MacroProcessor.jl +++ b/test/MQuery/MQuery.MacroProcessor.jl @@ -10,7 +10,7 @@ function flatten_macros(node :: Expr) Expr(:tuple, args...) || a && Do(args = [a]) => @match args begin - [args..., function ismacro end && tl] => [(op |> get_op, args), flatten_macros(tl)...] + [args..., GuardBy(ismacro) && tl] => [(op |> get_op, args), flatten_macros(tl)...] _ => [(op |> get_op, args)] end diff --git a/test/active_patterns.jl b/test/active_patterns.jl index 5847682..09d0367 100644 --- a/test/active_patterns.jl +++ b/test/active_patterns.jl @@ -1,27 +1,32 @@ -@testset "active pattern" begin +@testcase "active pattern" begin @testset "regular case" begin - @active LessThan0(x) begin + @lift @active LessThan0(x) begin if x > 0 nothing else - x + Some(x) end end - @test (@match 15 begin + b = (@match 15 begin LessThan0(_) => :a _ => :b - end) === :b + end) + + + @test b === :b @test (@match -15 begin LessThan0(a) => a _ => 0 end) == -15 end + @testset "parametric case" begin - @active Re{r :: Regex}(x) begin - match(r, x) + @lift @active Re{r :: Regex}(x) begin + ret = match(r, x) + ret === nothing || return Some(ret) end @test (@match "123" begin @@ -38,16 +43,17 @@ @testset "custom pattern for given structs" begin @eval struct Interval end - @active internal Interval{a, b}(arg) begin + @lift @active internal Interval{a, b}(arg) begin a <= arg <= b end @use Enum - @active visible in (@__MODULE__) IsEven(a) begin + @lift @active visible in (@__MODULE__) IsEven(a) begin a % 2 === 0 end + @lift MLStyle.is_enum(::Type{IsEven}) = true function parity(x) @match x begin IsEven => :even diff --git a/test/adt.jl b/test/adt.jl index f8734b4..e987558 100644 --- a/test/adt.jl +++ b/test/adt.jl @@ -1,56 +1,55 @@ - -module ADTSubTyping - using MLStyle - using Test - abstract type A{a} end - abstract type B{a} <: A{a} end +@testcase "subtyping" begin + @lift abstract type A{a} end + @lift abstract type B{a} <: A{a} end @testset "adt subtying" begin - @data C{A} <: B{A} begin + @lift @data C{A} <: B{A} begin C1(A, Int) end @test C1(1, 2) isa C{Int} - end - end -@testset "adt" begin - @testset "adt List" begin - # 1. define List - @data List{T} begin - Nil() - Cons(head :: T, tail :: List{T}) - end - # 2. define interfaces - len(xs::List{T}) where T = @match xs begin - Nil{T}() => 0 - Cons{T}(_, tail) => 1 + len(tail) - end +@testcase "adt list" begin + # 1. define the List data type + @lift @data List{T} begin + Nil() + Cons(head :: T, tail :: List{T}) + end + # 2. define interfaces + len(xs::List{T}) where T = @match xs begin + Nil{T}() => 0 + Cons{T}(_, tail) => 1 + len(tail) + end + @testset "adt List" begin @test len(Nil{Any}()) == 0 xs = Cons(3,Cons(2, Cons(1, Nil{Int}()))) @test len(xs) == 3 end - @testset "adt Arith" begin - # 1. define Arith - @data Arith begin - Num(v :: Int) - Minus(fst :: Arith, snd :: Arith) - Add(fst :: Arith, snd :: Arith) - Mult(fst :: Arith, snd :: Arith) - Divide(fst :: Arith, snd :: Arith) - end - # 2. define interfaces - function eval_arith(arith :: Arith) - @match arith begin - Num(v) => v - Add(fst, snd) => eval_arith(fst) + eval_arith(snd) - Minus(fst, snd) => eval_arith(fst) - eval_arith(snd) - Mult(fst, snd) => eval_arith(fst) * eval_arith(snd) - Divide(fst, snd) => eval_arith(fst) / eval_arith(snd) - end +end + +@testcase "adt arith" begin + # 1. define Arith + @lift @data Arith begin + Num(v :: Int) + Minus(fst :: Arith, snd :: Arith) + Add(fst :: Arith, snd :: Arith) + Mult(fst :: Arith, snd :: Arith) + Divide(fst :: Arith, snd :: Arith) + end + # 2. define interfaces + function eval_arith(arith :: Arith) + @match arith begin + Num(v) => v + Add(fst, snd) => eval_arith(fst) + eval_arith(snd) + Minus(fst, snd) => eval_arith(fst) - eval_arith(snd) + Mult(fst, snd) => eval_arith(fst) * eval_arith(snd) + Divide(fst, snd) => eval_arith(fst) / eval_arith(snd) end + end + + @testset "adt Arith" begin Number = Num @test eval_arith( Add(Number(1), @@ -59,35 +58,36 @@ end Mult(Number(2), Number(5)))))) == 1 end - end -@testset "case" begin - @data CD begin + +@testcase "share data with several modules" begin + @lift @data CD begin D(a, b) C{T} :: (a :: Int, b::T) => CD end - @data A begin + @lift @data A begin E() end - @test E <: A - @test fieldnames(D) == (:a, :b) - @test_throws MethodError C(3.0, :abc) -end + @testset "case" begin + @test E <: A + @test fieldnames(D) == (:a, :b) + @test_throws MethodError C(3.0, :abc) + end -module ADummy - using MLStyle -end + @lift module ADummy + using MLStyle + end -module BDummy - using MLStyle -end + @lift module BDummy + using MLStyle + end -@testset "share data with several modules" begin - @data visible in [ADummy] SSS begin + @lift @data visible in [ADummy] SSS begin SSS_1(Int) end + ADummy.eval(:(SSS_1 = $SSS_1; SSS = $SSS)) @test ADummy.eval(quote @@ -96,7 +96,6 @@ end end end) == :ok - BDummy.eval(:(SSS_1 = $SSS_1; SSS = $SSS)) @test_skip BDummy.eval(quote diff --git a/test/as_record.jl b/test/as_record.jl index 2ac2eef..f2889b9 100644 --- a/test/as_record.jl +++ b/test/as_record.jl @@ -21,6 +21,21 @@ end _ => false end +@test @match Record_1(1, 2) begin + Record_1(;a=1) => true + _ => false +end + +@test @match Record_1(1, 2) begin + Record_1(;b = 2) => true + _ => false +end + +@test @match Record_1(1, 2) begin + Record_1(;b) => b == 2 + _ => false +end + struct Record_2{A} x :: A y :: Int diff --git a/test/dot_expression.jl b/test/dot_expression.jl index 7a6482d..ce7eae8 100644 --- a/test/dot_expression.jl +++ b/test/dot_expression.jl @@ -8,7 +8,7 @@ macro linq(expr) @match expr begin :($subject.$method($(args...))) => let method = getfield(Linq, method) - quote $method($subject, $(args...)) end + :($method($subject, $(args...))) end _ => @error "invalid" end diff --git a/test/exception.jl b/test/exception.jl index ccbc78f..cb84baf 100644 --- a/test/exception.jl +++ b/test/exception.jl @@ -1,6 +1,6 @@ using MLStyle.Extension -@testset "exception" begin +@testcase "exception" begin @test_skip @match 1 begin Unknown(a, b) => 0 end @@ -13,10 +13,7 @@ using MLStyle.Extension 1 = 1 end)) - - @test_throws UnknownExtension used(:FieldPuns, MODULE) - - @data Test_Ext_Data begin + @lift @data Test_Ext_Data begin Test_Ext_Data_1 :: Int => Test_Ext_Data end diff --git a/test/format.jl b/test/format.jl new file mode 100644 index 0000000..2bcb174 --- /dev/null +++ b/test/format.jl @@ -0,0 +1,92 @@ +module Render +using Base: get +export render, @format, format, rmlines + +struct Discard end +const discard = Discard() + +function render(expr, pair :: Pair{Symbol, Any}) + render(expr, Dict(pair)) +end + +# function rmlines(expr::Expr) +# let args = filter(map(rmlines, expr.args)) do arg +# arg !== discard +# end +# Expr(expr.head, args...) +# end +# end + +# function rmlines(::LineNumberNode) +# discard +# end + +# function rmlines(a) +# a +# end + +function render(expr, config :: Dict{Symbol, Any}) + function visit(expr :: Expr) + hd = expr.head + tl = filter(x -> x !== discard, map(visit, expr.args)) + Expr(hd, tl...) + end + + function visit(sym :: Symbol) + get(config, sym) do + sym + end + end + + function visit(:: LineNumberNode) + discard + end + + function visit(a) + a + end + + visit(expr) +end + +function format(args, template) + function dispatch(arg :: Symbol) + Expr(:call, :(=>), QuoteNode(arg), arg) + end + + function dispatch(arg :: Pair) + Expr(:call, :(=>), QuoteNode(arg[1]), arg[2]) + end + + function dispatch(arg :: Expr) + @assert arg.head == :(=) + sym = arg.args[1] + @assert sym isa Symbol "$sym" + value = arg.args[2] + Expr(:call, :(=>), QuoteNode(sym), value) + end + function dispatch(_) + throw("Unknown argtype") + end + + constlist = map(dispatch, args.args) + constlist = Expr(:vect, constlist...) + config = Expr(:call, Dict{Symbol, Any}, constlist) + + wrap = @static VERSION < v"1.1.0" ? (x -> x) : (x -> Expr(:call, merge, :(Base.@locals), x)) + Expr(:call, render, template, wrap(config)) +end + +function format(template) + Expr(:call, render, template, :(Base.@locals)) +end + +macro format(args, template) + esc(format(args, template)) +end + +macro format(template) + esc(format(template)) +end + +end \ No newline at end of file diff --git a/test/pattern.jl b/test/pattern.jl index e786e79..0c18121 100644 --- a/test/pattern.jl +++ b/test/pattern.jl @@ -1,16 +1,16 @@ -@testset "pattern" begin +@testcase "pattern" begin @testset "ADT destructing" begin - @data internal Case begin + @lift @data internal Case begin Natural(latitude :: Float32, longitude :: Float32, climate :: String, dimension :: Int32) - Cutural(area :: String, kind :: String, country :: String, nature :: Natural) + Cultural(area :: String, kind :: String, country :: String, nature :: Natural) end - 神农架 = Cutural("湖北", "林区", "中国", Natural(31.744, 110.68, "北亚热带季风气候", 3106)) + 神农架 = Cultural("湖北", "林区", "中国", Natural(31.744, 110.68, "北亚热带季风气候", 3106)) - function my_data_query(data_lst :: Vector{Cutural}) + function my_data_query(data_lst :: Vector{Cultural}) filter(data_lst) do data @match data begin - Cutural(kind="林区", country="中国", + Cultural(kind="林区", country="中国", nature=Natural(latitude=latitude, longitude=longitude, dimension=dim)) && if latitude > 30.0 && longitude > 100 && dim > 1000 end => true diff --git a/test/pervasive.jl b/test/pervasive.jl index 861a044..cfb5a4f 100644 --- a/test/pervasive.jl +++ b/test/pervasive.jl @@ -1,4 +1,4 @@ -@testset "Int" begin +@testcase "Int" begin @testset "affirm: 1-10" begin @test all(1:10) do a @match a begin @@ -124,8 +124,8 @@ end end -@testset "Recognizer(AppPattern)" begin - @data internal TestRecog begin +@testcase "Recognizer(AppPattern)" begin + @lift @data internal TestRecog begin TestRecog_A(Int, Int) TestRecog_B(a :: Float64, b :: String) end @@ -173,11 +173,12 @@ end end -struct TestGH end -@testset "Generalized Recognizer(GAppPattern)" begin + +@testcase "Generalized Recognizer(GAppPattern)" begin @use GADT - @data internal TestGRecog{T} begin + @lift struct TestGH end + @lift @data internal TestGRecog{T} begin TestGRecog_A{T, A} :: (A, T) => TestGRecog{T} TestGRecog_B{T, B} :: (a :: T, b :: B) => TestGRecog{T} end @@ -245,9 +246,7 @@ struct TestGH end end -module TestUppercaseCapturing - using Test - using MLStyle +@testcase "TestUppercaseCapturing" begin @testset "UppercaseCapturing" begin @use UppercaseCapturing diff --git a/test/readme_codes.jl b/test/readme_codes.jl index 006a139..397425e 100644 --- a/test/readme_codes.jl +++ b/test/readme_codes.jl @@ -1,6 +1,5 @@ rmlines = @λ begin - e :: Expr -> Expr(e.head, filter(x -> x !== nothing, map(rmlines, e.args))...) - :: LineNumberNode -> nothing + e :: Expr -> Expr(e.head, map(rmlines, filter(x -> !(x isa LineNumberNode), e.args))...) a -> a end expr = quote @@ -10,7 +9,7 @@ expr = quote end end |> rmlines -@match expr begin +@test @match expr begin quote struct $name{$tvar} $f1 :: $t1 diff --git a/test/render.jl b/test/render.jl deleted file mode 100644 index 02b5d44..0000000 --- a/test/render.jl +++ /dev/null @@ -1,21 +0,0 @@ -using MLStyle.Render -module S_test_render - b = 5 - node = :(function func_name(a) - a + b - end) -end -@testset "ast interpolation" begin - x = 1 - y = 2 - - node = @format [b = x + y] quote - b + 2 - end - @test eval(node) == 5 - - S = S_test_render - S.eval(render(S.node, Dict{Symbol, Any}(:b => 2, :func_name => :f1))) - @test S.f1(1) == 3 - -end diff --git a/test/runtests.jl b/test/runtests.jl index 0f5385c..830532c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,41 +3,76 @@ module TestModule using Test using MLStyle +liftsym = Symbol("@lift") + +function lift!(ex, lifted::Vector{Any}) + @switch ex begin + @case Expr(:macrocall, &liftsym, _, arg) + push!(lifted, arg) + ex.args + ex.args[1] = Symbol("@static") + ex.args[3] = :(true ? true : true) + return + @case Expr(hd, args...) + for arg in args + lift!(arg, lifted) + end + return + @case _ + return + end +end + +macro testcase(name, ex) + lifted = [] + lift!(ex, lifted) + m = gensym(name) + __module__.eval(:(module $m + using MLStyle + using Test + MODULE = $MODULE + $(Symbol("@test_macro_throws")) = $(getfield(TestModule, Symbol("@test_macro_throws"))) + $(lifted...) + @testset $name $ex + end)) +end + macro test_macro_throws(errortype, m) - :( - @test_throws $errortype try - @eval $m - catch err - while err isa LoadError - err = err.error - end - throw(err) - end - ) + :(@test_throws $errortype try + @eval $m + catch err + while err isa LoadError + err = err.error + end + throw(err) + end) end MODULE = TestModule @use GADT -include("uncomp.jl") + include("when.jl") -include("MQuery/test.jl") -include("modules/cond.jl") -include("modules/ast.jl") +include("untyped_lam.jl") +include("active_patterns.jl") +include("uncomp.jl") +include("lambda.jl") include("as_record.jl") include("adt.jl") -include("active_patterns.jl") + include("exception.jl") -include("render.jl") -include("pervasive.jl") include("expr_template.jl") include("gallery/simple.jl") +include("dot_expression.jl") + +include("modules/cond.jl") +include("modules/ast.jl") + +include("pervasive.jl") include("match.jl") include("pattern.jl") -include("dot_expression.jl") include("typelevel.jl") -include("untyped_lam.jl") include("nothing.jl") -include("lambda.jl") -end \ No newline at end of file +include("MQuery/test.jl") +end diff --git a/test/typelevel.jl b/test/typelevel.jl index 1b2b168..173f67e 100644 --- a/test/typelevel.jl +++ b/test/typelevel.jl @@ -1,5 +1,5 @@ -@testset "type destructing" begin - @data internal S{A, B} begin +@testcase "type destructing" begin + @lift @data internal S{A, B} begin S_1{A, B} :: (a :: A, b :: B) => S{A, B} end diff --git a/test/untyped_lam.jl b/test/untyped_lam.jl index c5378f5..b29c411 100644 --- a/test/untyped_lam.jl +++ b/test/untyped_lam.jl @@ -2,74 +2,75 @@ import Base: convert -struct Fun{T, R} - fn :: Function +struct Fun{T,R} + fn::Function end -function (typed_fn :: Fun{T, R})(arg :: T) :: R where {T, R} +function (typed_fn::Fun{T,R})(arg::T)::R where {T,R} typed_fn.fn(arg) end -function convert(::Type{Fun{T, R}}, fn :: Function) where {T, R} - Fun{T, R}(fn) +function convert(::Type{Fun{T,R}}, fn::Function) where {T,R} + Fun{T,R}(fn) end -function convert(::Type{Fun{T, R}}, fn :: Fun{C, D}) where{T, R, C <: T, D <: R} - Fun{T, R}(fn.fn) +function convert(::Type{Fun{T,R}}, fn::Fun{C,D}) where {T,R,C<:T,D<:R} + Fun{T,R}(fn.fn) end -⇒(::Type{A}, ::Type{B}) where {A, B} = Fun{A, B} +⇒(::Type{A}, ::Type{B}) where {A,B} = Fun{A,B} @data public Exp{T} begin - Sym :: Symbol => Exp{A} where {A} - Val{A} :: A => Exp{A} - App{A, B, A_ <: A} :: (Exp{Fun{A, B}}, Exp{A_}) => Exp{B} - Lam{A, B} :: (Symbol, Exp{B}) => Exp{Fun{A, B}} - If{A} :: (Exp{Bool}, Exp{A}, Exp{A}) => Exp{A} + Sym{T}::Symbol => Exp{T} + Val{A}::A => Exp{A} + App{A,B}::(Exp{Fun{A,B}}, Exp{<:A}) => Exp{B} + Lam{A,B}::(Symbol, Exp{B}) => Exp{Fun{A,B}} + If{A}::(Exp{Bool}, Exp{A}, Exp{A}) => Exp{A} end -function substitute(template :: Exp{T}, pair :: Tuple{Symbol, Exp{G}}) where {T, G} +function substitute(template::Exp{T}, pair::Tuple{Symbol,Exp{G}}) where {T,G<:T} (sym, exp) = pair @match template begin Sym(&sym) => exp Val(_) => template - App(f, a) => App(substitute(f, pair), substitute(a, pair)) :: Exp{T} + App(f, a) => App(substitute(f, pair), substitute(a, pair)) Lam(&sym, exp) => template - If(cond, exp1, exp2) => - let (cond, exp1, exp2) = map(substitute, (cond, exp1, exp2)) - If(cond, exp1, exp2) :: Exp{T} - end + Lam(sym, exp) => Lam(sym, substitute(exp, pair)) + If(cond, exp1, exp2) => begin + (cond, exp1, exp2) = + substitute(cond, pair), substitute(exp1, pair), substitute(exp2, pair) + If(cond, exp1, exp2) + end end end -function eval_exp(exp :: Exp{T}, ctx :: Dict{Symbol, Any}) where T +function eval_exp(exp::Exp{T_}, ctx::Dict{Symbol,Any}) where {T_} + let T = T_ # fix static parameter issues for Julia 1.0 and 1.1 @match exp begin - Sym(a) => (ctx[a] :: T, ctx) - Val(a :: T) => (a, ctx) - App{A, T, A_}(f :: Exp{Fun{A, T}}, arg :: Exp{A_}) where {A, A_ <: A} => - let (f, ctx) = eval_exp(f, ctx), - (arg, ctx) = eval_exp(arg, ctx) + Sym(a) => (ctx[a]::T, ctx) + Val(a::T) => (a, ctx) + App{A,T}(f::Exp{Fun{A,T}}, arg::Exp{<:A}) where {A, T} => + let (f, ctx) = eval_exp(f, ctx), (arg, ctx) = eval_exp(arg, ctx) (f(arg), ctx) end - Lam{A, B}(sym, exp::Exp{B}) where {A, B} => - let f(x :: A) = begin - A + Lam{A,B}(sym, exp::Exp{B}) where {A,B} => + let f(x) = begin eval_exp(substitute(exp, sym => Val(x)), ctx)[1] end - (f, ctx) end - If(cond, exp1, exp2) => - let (cond, ctx) = eval_exp(cond, ctx) - eval_exp(cond ? exp1 : exp2, ctx) - end + If(cond, exp1, exp2) => let (cond, ctx) = eval_exp(cond, ctx) + eval_exp(cond ? exp1 : exp2, ctx) + end + _ => error(exp) + end end end add = Val{Number ⇒ Number ⇒ Number}(x -> y -> x + y) sub = Val{Number ⇒ Number ⇒ Number}(x -> y -> x - y) gt = Val{Number ⇒ Number ⇒ Bool}(x -> y -> x > y) -ctx = Dict{Symbol, Any}() +ctx = Dict{Symbol,Any}() @assert 3 == eval_exp(App(App(add, Val(1)), Val(2)), ctx)[1] @assert -1 == eval_exp(App(App(sub, Val(1)), Val(2)), ctx)[1] @@ -77,5 +78,7 @@ ctx = Dict{Symbol, Any}() If( App(App(gt, Sym{Int}(:x)), Sym{Int}(:y)), App(App(sub, Sym{Int}(:x)), Sym{Int}(:y)), - App(App(sub, Sym{Int}(:y)), Sym{Int}(:x)) - ), Dict{Symbol, Any}(:x => 1, :y => 2))[1] + App(App(sub, Sym{Int}(:y)), Sym{Int}(:x)), + ), + Dict{Symbol,Any}(:x => 1, :y => 2), +)[1] diff --git a/test/when.jl b/test/when.jl index fe3bab3..1f9fcb5 100644 --- a/test/when.jl +++ b/test/when.jl @@ -1,4 +1,9 @@ -@testset "@when" begin +@testcase "@when" begin + @lift @data WhenTest begin + WhenTest_1(Int) + WhenTest_2(Int) + end + @testset "docstring" begin # otherwise() @test 3 == @when (a, b) = (1, 2) begin @@ -72,10 +77,6 @@ end @testset "@when + @data" begin - @data WhenTest begin - WhenTest_1(Int) - WhenTest_2(Int) - end var1 = WhenTest_1(2) var2 = WhenTest_2(2)