Skip to content

Commit

Permalink
Merge pull request #11 from mcass19/add_function_def
Browse files Browse the repository at this point in the history
Add function def
  • Loading branch information
mcass19 authored Apr 20, 2020
2 parents 5e34a33 + 71864ed commit 64b3db7
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 93 deletions.
1 change: 1 addition & 0 deletions lib/typelixir.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ defmodule Typelixir do
defp compile_file(path, modules_functions) do
env = %{
state: :ok,
type: nil,
error_data: %{},
warnings: %{},
data: %{},
Expand Down
2 changes: 2 additions & 0 deletions lib/typelixir/modules_name_extractor.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
defmodule Typelixir.ModuleNamesExtractor do
@moduledoc false

# returns the map %{module_name => path_in_which_the_module_is}

def extract_modules_names(all_paths) do
modules_paths =
Expand Down
2 changes: 2 additions & 0 deletions lib/typelixir/pre_processor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule Typelixir.PreProcessor do

alias Typelixir.{TypeBuilder}

# extends the given map with the module name as key and the typed functions it defines as value

def process_file(path, modules_functions) do
ast = Code.string_to_quoted(File.read!(Path.absname(path)))
env = %{
Expand Down
261 changes: 176 additions & 85 deletions lib/typelixir/processor.ex

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions lib/typelixir/type_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ defmodule Typelixir.TypeBuilder do

alias Typelixir.TypeComparator

# ---------------------------------------------------------------------------------------------------
# build -> returns the type of any expression

@operators [:*, :+, :/, :-, :and, :or, :not, :++, :--, :<>]
@comparison_operators [:==, :!=, :===, :!==, :>, :<, :>=, :<=]

Expand Down Expand Up @@ -89,6 +92,7 @@ defmodule Typelixir.TypeBuilder do
end

# ---------------------------------------------------------------------------------------------------
# add_variables -> returns a new environment with the vars of pattern1 and the types of pattern2 (used by binding)

def add_variables(_, _, _, nil, env), do: env[:vars]

Expand Down Expand Up @@ -126,4 +130,43 @@ defmodule Typelixir.TypeBuilder do
end

def add_variables(_, _, _, _, env), do: env[:vars]

# ---------------------------------------------------------------------------------------------------
# get_new_vars_env -> returns a new map with the vars of params and the corresponding types of
# param_type_list (used by function definition)

def get_new_vars_env(params, param_type_list) do
new_vars = Enum.zip(params, param_type_list) |> Enum.map(fn {var, type} -> get_new_vars(var, type) end) |> List.flatten()
case Enum.member?(new_vars, :error) do
true -> :error
_ ->
Enum.reduce(new_vars, %{}, fn {var, type}, acc -> Map.put(acc, var, type) end)
end
end

# List
defp get_new_vars(op, {:list, type}) when is_list(op), do: Enum.map(op, fn x -> get_new_vars(x, type) end)

defp get_new_vars(_, nil), do: []

defp get_new_vars({op, _, _}, type) when (op not in [:{}, :%{}]), do: {op, type}

# Tuple
defp get_new_vars({:{}, _, ops}, {:tuple, type_list}) do
if length(ops) === length(type_list), do: Enum.zip(ops, type_list) |> Enum.map(fn {var, type} -> get_new_vars(var, type) end),
else: :error
end

defp get_new_vars(ops, {:tuple, type_list}) do
ops = Tuple.to_list(ops)
if length(ops) === length(type_list), do: Enum.zip(ops, type_list) |> Enum.map(fn {var, type} -> get_new_vars(var, type) end),
else: :error
end

# Map
defp get_new_vars({:%{}, _, op}, {:map, {_, value_type}}) do
(Enum.map(op, fn {_, value} -> value end) |> Enum.map(fn x -> get_new_vars(x, value_type) end))
end

defp get_new_vars(_, _), do: :error
end
13 changes: 7 additions & 6 deletions lib/typelixir/type_comparator.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
defmodule Typelixir.TypeComparator do
# ---------------------------------------------------------------------------------------------------
# less_or_equal? -> returns true if type1 is less or equal than type2

def less_or_equal?(type1, type2) when type1 === type2, do: true

def less_or_equal?(list_type1, list_type2) when (is_list(list_type1) and is_list(list_type2)) do
Expand Down Expand Up @@ -28,6 +31,7 @@ defmodule Typelixir.TypeComparator do
def less_or_equal?(_, _), do: :error

# ---------------------------------------------------------------------------------------------------
# greater -> returns the greater between type1 and type2

def greater(type1, type2) when type1 === type2, do: type1

Expand Down Expand Up @@ -60,6 +64,7 @@ defmodule Typelixir.TypeComparator do
def greater(_, _), do: :error

# ---------------------------------------------------------------------------------------------------
# has_type? -> returns true if type1 contains type2

def has_type?(list_type, type) when is_list(list_type) do
Enum.map(list_type, fn t -> has_type?(t, type) end) |> Enum.member?(true)
Expand All @@ -77,9 +82,7 @@ defmodule Typelixir.TypeComparator do
def has_type?(_, _), do: false

# ---------------------------------------------------------------------------------------------------

# float_to_int? returns true if op1 has type integer and op2 has type float because means that
# we are binding a float to an integer and that is an error
# float_to_int? returns true if pattern1 has type integer and the corresponding pattern2 has type float

# Literals
def float_to_int?(op1, op2, _env) when (is_integer(op1) and is_float(op2)), do: true
Expand Down Expand Up @@ -125,9 +128,7 @@ defmodule Typelixir.TypeComparator do
def float_to_int?(_, _, _), do: false

# ---------------------------------------------------------------------------------------------------

# float_to_int_type? returns true if op1 has type integer and op2 has type float because means that
# we are binding a float to an integer and that is an error
# float_to_int_type? returns true if type1 has type integer and the corresponding type2 is float

def float_to_int_type?(list_type1, list_type2) when (is_list(list_type1) and is_list(list_type2)) do
if (length(list_type1) === length(list_type2)),
Expand Down
5 changes: 3 additions & 2 deletions test/typelixir/processor_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule Typelixir.ProcessorTest do

@env %{
state: :ok,
type: nil,
error_data: %{},
warnings: %{},
data: [],
Expand All @@ -26,14 +27,14 @@ defmodule Typelixir.ProcessorTest do
test "returns ok when there is no module or code defined on the file" do
File.write("test/tmp/example.ex", "")
assert Processor.process_file("#{@test_dir}/example.ex", @env)
=== %{data: %{}, error_data: %{}, warnings: %{}, module_name: :empty, modules_functions: %{}, state: :ok, vars: %{}}
=== %{data: %{}, type: nil, error_data: %{}, warnings: %{}, module_name: :empty, modules_functions: %{}, state: :ok, vars: %{}}

File.write("test/tmp/example.ex", "
defmodule Example do
end
")
assert Processor.process_file("#{@test_dir}/example.ex", @env)
=== %{data: %{}, error_data: %{}, warnings: %{}, module_name: :Example, modules_functions: %{Example: %{}}, state: :ok, vars: %{}}
=== %{data: %{}, type: nil, error_data: %{}, warnings: %{}, module_name: :Example, modules_functions: %{Example: %{}}, state: :ok, vars: %{}}
end

# TO DO ALL THE TEST CASES WE WANT
Expand Down
57 changes: 57 additions & 0 deletions test/typelixir/type_builder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,62 @@ defmodule Typelixir.TypeBuilderTest do
%{a: :integer, b: :string, c: {:tuple, [{:list, :integer}, :string]}, d: {:list, :integer}, y: {:list, :integer}, z: {:list, :integer}}
end
end

describe "get_new_vars_env" do
test "returns error when variables doesn't match with the corresponding type" do
assert TypeBuilder.get_new_vars_env([{:x, [line: 10], nil}], [nil])
=== %{}

assert TypeBuilder.get_new_vars_env([{:x, [line: 10], nil}], [])
=== %{}

assert TypeBuilder.get_new_vars_env([[{:x, [line: 10], nil}, {:z, [line: 10], nil}]], [:integer])
=== :error

assert TypeBuilder.get_new_vars_env([{{:x, [line: 10], nil}, {:z, [line: 10], nil}}, {:y, [line: 10], nil}],
[:float, :integer])
=== :error

assert TypeBuilder.get_new_vars_env([{{:x, [line: 10], nil}, {:z, [line: 10], nil}}, {:y, [line: 10], nil}],
[{:list, :float}, :integer])
=== :error

assert TypeBuilder.get_new_vars_env([{:%{}, [line: 10], [{1, {:y, [line: 10], nil}}]}], [{:tuple, [:integer, :string]}])
=== :error

assert TypeBuilder.get_new_vars_env([{:%{}, [line: 10], [{1, {:y, [line: 10], nil}}]}], [:atom])
=== :error
end

test "returns variables with the corresponding type" do
assert TypeBuilder.get_new_vars_env([{:x, [line: 10], nil}], [:integer])
=== %{x: :integer}

assert TypeBuilder.get_new_vars_env([{:x, [line: 10], nil}], [nil])
=== %{}

assert TypeBuilder.get_new_vars_env([{:a, [line: 10], nil}], [:boolean])
=== %{a: :boolean}

assert TypeBuilder.get_new_vars_env([{:x, [line: 10], nil}], [{:tuple, [{:list, :integer}, :string]}])
=== %{x: {:tuple, [{:list, :integer}, :string]}}

assert TypeBuilder.get_new_vars_env([{:x, [line: 10], nil}, {:z, [line: 10], nil}], [:integer, :float])
=== %{x: :integer, z: :float}

assert TypeBuilder.get_new_vars_env([[{:x, [line: 10], nil}, {:z, [line: 10], nil}]], [{:list, :float}])
=== %{x: :float, z: :float}

assert TypeBuilder.get_new_vars_env([{{:x, [line: 10], nil}, {:z, [line: 10], nil}}, {:y, [line: 10], nil}],
[{:tuple, [{:tuple, [{:list, :integer}, :string]}, :float]}, :integer])
=== %{x: {:tuple, [{:list, :integer}, :string]}, y: :integer, z: :float}

assert TypeBuilder.get_new_vars_env([{:x, [line: 10], nil}, {:z, [line: 10], nil}], [{:map, {:integer, :string}}, :float])
=== %{x: {:map, {:integer, :string}}, z: :float}

assert TypeBuilder.get_new_vars_env([{:%{}, [line: 10], [{1, {:y, [line: 10], nil}}]}], [{:map, {:integer, :string}}])
=== %{y: :string}
end
end
end

0 comments on commit 64b3db7

Please sign in to comment.