class ExhaustivenessChecker::Compiler

Defined in:

exhaustiveness_checker.cr

Constructors

Instance Method Summary

Constructor Detail

def self.new(variant_lookup : ExhaustivenessChecker::Type | ExhaustivenessChecker::TypeVariable -> Nil | Array(ExhaustivenessChecker::Variant), variant_name_lookup : String, Int32 -> Nil | String) #

[View source]

Instance Method Detail

def branch_mode(all_rows : Array(Row)) : BranchMode #

Given a row, returns the kind of branch that is referred to the most across all rows.


[View source]
def compile(rows : Array(Row)) : Match #

[View source]
def compile_array_cases(rows : Array(Row), branch_var : Variable, type : Checkable) : Decision #

Decises array matches from the given rows.


[View source]
def compile_constructor_cases(rows : Array(Row), branch_var : Variable, cases : Array(Tuple(Constructor, Array(Variable), Array(Row)))) : Array(Case) #

Compiles the cases and sub cases for the constructor located at the column of the branching variable.

What exactly this method does may be a bit hard to understand from the code, as there's simply quite a bit going on. Roughly speaking, it does the following:

  1. It takes the column we're branching on (based on the branching variable) and removes it from every row.

  2. We add additional columns to this row, if the constructor takes any arguments (which we'll handle in a nested match).

  3. We turn the resulting list of rows into a list of cases, then compile those into decision (sub) trees.

If a row didn't include the branching variable, we copy that row into the list of rows for every constructor to test.

For this to work, the cases variable must be prepared such that it has a triple for every constructor we need to handle. For an ADT with 10 constructors, that means 10 triples. This is needed so this method can assign the correct sub matches to these constructors.

Types with infinite constructors (e.g. values) are handled separately; they don't need most of this work anyway.


[View source]
def compile_infinite_cases(rows : Array(Row), branch_var : Variable) : Tuple(Array(Case), Decision) #

Values have an infinite number of constructors, so we specialise the compilation of their patterns with this function.

What this does is basically marks branches the same values unreachable.

case "a" { "A" => "" "A" => "" // here...this is unreachable _ => "" }


[View source]
def compile_rows(rows : Array(Row)) : Decision #

[View source]
def constructor_index(item : Constructor) : Int32 #

Returns the index of the constructor.


[View source]
def diagnostics : Diagnostics #

The diagnostics.


[View source]
def flatten_or(pattern : Pattern, row : Row) : Array(Tuple(Pattern, Row)) #

Flattens POr patterns (left, then right).


[View source]
def move_unconditional_patterns(row : Row) : Row #

Moves variable-only patterns/tests into the right-hand side/body of a case.

This turns cases like this:

case one -> print(one)
case _ -> print("nothing")

Into this:

case -> {
    let one = it
    print(one)
}
case -> {
    print("nothing")
}

Where it is a variable holding the value case one is compared against, and the case/row has no patterns (i.e. always matches).


[View source]
def new_variable(type : Checkable) : Variable #

Returns a new variable to use in the decision tree.


[View source]
def new_variables(types : Array(Checkable)) : Array(Variable) #

Returns a new variables to use in the decision tree.


[View source]
def variable_id : Int32 #

The current counter for variables.


[View source]
def variant_lookup : Proc(Checkable, Array(Variant) | Nil) #

[View source]
def variant_name_lookup : Proc(String, Int32, String | Nil) #

[View source]