low_type 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/adapters/sinatra_adapter.rb +3 -3
- data/lib/factories/proxy_factory.rb +2 -2
- data/lib/{parser.rb → file_parser.rb} +27 -10
- data/lib/local_types.rb +19 -14
- data/lib/low_type.rb +11 -14
- data/lib/proxies/local_proxy.rb +1 -1
- data/lib/proxies/param_proxy.rb +1 -1
- data/lib/proxies/return_proxy.rb +1 -1
- data/lib/redefiner.rb +44 -18
- data/lib/{subclasses.rb → syntax.rb} +14 -30
- data/lib/type_expression.rb +2 -2
- data/lib/{error_types.rb → types/error_types.rb} +1 -0
- data/lib/version.rb +1 -1
- metadata +5 -5
- /data/lib/{basic_types.rb → types/complex_types.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9a921b130ff71b7911409536acb53ad6ef7abebf91d789ce7ce27fde24b90e95
|
|
4
|
+
data.tar.gz: 9138f98a23846c1bb2474d2bb7b163dd0b0575e7c5911dd4b7ba05fc9deacb48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a440d93c18f7e444293f6987b5655644856d15ebba9a32b6d01818e9546339d48d8b48f5b09023e3039271195c790ce6ec713ca2b05498202cfd45d628c12e2a
|
|
7
|
+
data.tar.gz: '039ca399f75d3ee60be9c73399dfb15cadaf62d200254cdc1c873130b595c7715917eda4fa2e926e986c66817e6de972701c5b21cdc2e68ec282da71d040b46a'
|
|
@@ -5,7 +5,7 @@ require 'prism'
|
|
|
5
5
|
require_relative '../interfaces/adapter_interface'
|
|
6
6
|
require_relative '../proxies/file_proxy'
|
|
7
7
|
require_relative '../proxies/return_proxy'
|
|
8
|
-
require_relative '../error_types'
|
|
8
|
+
require_relative '../types/error_types'
|
|
9
9
|
|
|
10
10
|
module LowType
|
|
11
11
|
module Adapter
|
|
@@ -27,7 +27,7 @@ module LowType
|
|
|
27
27
|
|
|
28
28
|
pattern = arguments_node.arguments.first.content
|
|
29
29
|
|
|
30
|
-
line =
|
|
30
|
+
line = FileParser.line_number(node: method_call)
|
|
31
31
|
file = FileProxy.new(path: @file_path, line:, scope: "#{@klass}##{method_call.name}")
|
|
32
32
|
next unless (return_proxy = return_proxy(method_node: method_call, pattern:, file:))
|
|
33
33
|
|
|
@@ -38,7 +38,7 @@ module LowType
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def return_proxy(method_node:, pattern:, file:)
|
|
41
|
-
return_type =
|
|
41
|
+
return_type = FileParser.return_type(method_node:)
|
|
42
42
|
return nil if return_type.nil?
|
|
43
43
|
|
|
44
44
|
# Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../proxies/return_proxy'
|
|
4
|
-
require_relative '../
|
|
4
|
+
require_relative '../file_parser'
|
|
5
5
|
require_relative '../type_expression'
|
|
6
6
|
|
|
7
7
|
module LowType
|
|
8
8
|
class ProxyFactory
|
|
9
9
|
class << self
|
|
10
10
|
def return_proxy(method_node:, file:)
|
|
11
|
-
return_type =
|
|
11
|
+
return_type = FileParser.return_type(method_node:)
|
|
12
12
|
return nil if return_type.nil?
|
|
13
13
|
|
|
14
14
|
# Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
|
|
@@ -3,26 +3,26 @@
|
|
|
3
3
|
require 'prism'
|
|
4
4
|
|
|
5
5
|
module LowType
|
|
6
|
-
class
|
|
7
|
-
attr_reader :parent_map, :instance_methods, :class_methods, :
|
|
6
|
+
class FileParser
|
|
7
|
+
attr_reader :parent_map, :instance_methods, :class_methods, :line_numbers
|
|
8
8
|
|
|
9
|
-
def initialize(file_path:)
|
|
9
|
+
def initialize(klass:, file_path:)
|
|
10
10
|
@root_node = Prism.parse_file(file_path).value
|
|
11
11
|
|
|
12
12
|
parent_mapper = ParentMapper.new
|
|
13
13
|
parent_mapper.visit(@root_node)
|
|
14
14
|
@parent_map = parent_mapper.parent_map
|
|
15
15
|
|
|
16
|
-
method_visitor = MethodDefVisitor.new(@parent_map)
|
|
16
|
+
method_visitor = MethodDefVisitor.new(root_node: @root_node, parent_map:, klass:)
|
|
17
17
|
@root_node.accept(method_visitor)
|
|
18
18
|
|
|
19
19
|
@instance_methods = method_visitor.instance_methods
|
|
20
20
|
@class_methods = method_visitor.class_methods
|
|
21
|
-
@
|
|
21
|
+
@line_numbers = method_visitor.line_numbers
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def method_calls(method_names:)
|
|
25
|
-
block_visitor = MethodCallVisitor.new(parent_map
|
|
25
|
+
block_visitor = MethodCallVisitor.new(parent_map:, method_names:)
|
|
26
26
|
@root_node.accept(block_visitor)
|
|
27
27
|
block_visitor.method_calls
|
|
28
28
|
end
|
|
@@ -54,14 +54,15 @@ module LowType
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
class MethodDefVisitor < Prism::Visitor
|
|
57
|
-
attr_reader :class_methods, :instance_methods, :
|
|
57
|
+
attr_reader :class_methods, :instance_methods, :line_numbers
|
|
58
58
|
|
|
59
|
-
def initialize(parent_map)
|
|
59
|
+
def initialize(root_node:, parent_map:, klass:)
|
|
60
60
|
@parent_map = parent_map
|
|
61
|
+
@klass = klass
|
|
61
62
|
|
|
62
63
|
@instance_methods = []
|
|
63
64
|
@class_methods = []
|
|
64
|
-
@
|
|
65
|
+
@line_numbers = { class_start: 0, class_end: root_node.end_line }
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
def visit_def_node(node)
|
|
@@ -75,7 +76,23 @@ module LowType
|
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
def visit_call_node(node)
|
|
78
|
-
|
|
79
|
+
start_line = node.name == :private && node.respond_to?(:start_line) && node.start_line || nil
|
|
80
|
+
class_start = @line_numbers[:class_start]
|
|
81
|
+
class_end = @line_numbers[:class_end]
|
|
82
|
+
|
|
83
|
+
if start_line && class_start && class_end && start_line > class_start && start_line < class_end
|
|
84
|
+
@line_numbers[:private_start] = node.start_line
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
super
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def visit_class_node(node)
|
|
91
|
+
if node.name == @klass.to_s.to_sym
|
|
92
|
+
@line_numbers = { class_start: node.class_keyword_loc.start_line, class_end: node.end_keyword_loc.end_line }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
super
|
|
79
96
|
end
|
|
80
97
|
|
|
81
98
|
private
|
data/lib/local_types.rb
CHANGED
|
@@ -2,27 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'proxies/file_proxy'
|
|
4
4
|
require_relative 'proxies/local_proxy'
|
|
5
|
+
require_relative 'types/error_types'
|
|
5
6
|
require_relative 'type_expression'
|
|
6
7
|
require_relative 'value_expression'
|
|
7
8
|
|
|
8
|
-
module
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
module LowType
|
|
10
|
+
module LocalTypes
|
|
11
|
+
def type(type_expression)
|
|
12
|
+
value = type_expression.default_value
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
last_caller = caller_locations(1, 1).first
|
|
15
|
+
file = FileProxy.new(path: last_caller.path, line: last_caller.lineno, scope: 'local type')
|
|
16
|
+
proxy = LocalProxy.new(type_expression:, name: self, file:)
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
type_expression.validate!(value:, proxy:)
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
return value.value if value.is_a?(ValueExpression)
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
value
|
|
23
|
+
rescue NoMethodError
|
|
24
|
+
raise ConfigError, "Invalid type expression, likely because you didn't add 'using LowType::Syntax'"
|
|
25
|
+
end
|
|
26
|
+
alias low_type type
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
def value(type)
|
|
29
|
+
LowType.value(type:)
|
|
30
|
+
end
|
|
31
|
+
alias low_value value
|
|
26
32
|
end
|
|
27
|
-
alias low_value value
|
|
28
33
|
end
|
data/lib/low_type.rb
CHANGED
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'adapters/adapter_loader'
|
|
4
|
-
require_relative '
|
|
4
|
+
require_relative 'types/complex_types'
|
|
5
5
|
require_relative 'instance_types'
|
|
6
6
|
require_relative 'local_types'
|
|
7
7
|
require_relative 'redefiner'
|
|
8
|
-
require_relative '
|
|
8
|
+
require_relative 'syntax'
|
|
9
9
|
require_relative 'type_expression'
|
|
10
10
|
require_relative 'value_expression'
|
|
11
11
|
|
|
12
|
-
# Include this module into your class to define and check types.
|
|
13
12
|
module LowType
|
|
14
13
|
# We do as much as possible on class load rather than on instantiation to be thread-safe and efficient.
|
|
15
|
-
def self.included(klass)
|
|
14
|
+
def self.included(klass)
|
|
16
15
|
class << klass
|
|
17
16
|
def low_methods
|
|
18
17
|
@low_methods ||= {}
|
|
19
18
|
end
|
|
20
19
|
end
|
|
21
20
|
|
|
22
|
-
file_path = LowType.file_path
|
|
23
|
-
parser =
|
|
24
|
-
|
|
21
|
+
file_path = LowType.file_path
|
|
22
|
+
parser = FileParser.new(klass:, file_path:)
|
|
23
|
+
line_numbers = parser.line_numbers
|
|
25
24
|
|
|
26
25
|
klass.extend InstanceTypes
|
|
27
26
|
klass.include LocalTypes
|
|
28
|
-
klass.prepend LowType::Redefiner.redefine(method_nodes: parser.instance_methods, klass:,
|
|
29
|
-
klass.singleton_class.prepend LowType::Redefiner.redefine(method_nodes: parser.class_methods, klass:,
|
|
27
|
+
klass.prepend LowType::Redefiner.redefine(method_nodes: parser.instance_methods, klass:, line_numbers:, file_path:)
|
|
28
|
+
klass.singleton_class.prepend LowType::Redefiner.redefine(method_nodes: parser.class_methods, klass:, line_numbers:, file_path:)
|
|
30
29
|
|
|
31
30
|
if (adapter = Adapter::Loader.load(klass:, parser:, file_path:))
|
|
32
31
|
adapter.process
|
|
@@ -48,11 +47,9 @@ module LowType
|
|
|
48
47
|
|
|
49
48
|
# Internal API.
|
|
50
49
|
|
|
51
|
-
def file_path
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# The first class found regardless of namespace will be the class that did the include.
|
|
55
|
-
caller.find { |callee| callee.end_with?("<class:#{class_name}>'") }.split(':').first
|
|
50
|
+
def file_path
|
|
51
|
+
includer_file = caller.find { |callee| callee.end_with?("in 'Module#include'") || callee.end_with?("in 'include'") }
|
|
52
|
+
includer_file.split(':').first
|
|
56
53
|
end
|
|
57
54
|
|
|
58
55
|
# TODO: Unit test.
|
data/lib/proxies/local_proxy.rb
CHANGED
data/lib/proxies/param_proxy.rb
CHANGED
data/lib/proxies/return_proxy.rb
CHANGED
data/lib/redefiner.rb
CHANGED
|
@@ -4,58 +4,80 @@ require_relative 'factories/proxy_factory'
|
|
|
4
4
|
require_relative 'proxies/file_proxy'
|
|
5
5
|
require_relative 'proxies/method_proxy'
|
|
6
6
|
require_relative 'proxies/param_proxy'
|
|
7
|
+
require_relative 'syntax'
|
|
7
8
|
require_relative 'type_expression'
|
|
8
9
|
|
|
9
10
|
module LowType
|
|
10
11
|
# Redefine methods to have their arguments and return values type checked.
|
|
11
12
|
class Redefiner
|
|
13
|
+
using Syntax
|
|
14
|
+
|
|
12
15
|
class << self
|
|
13
|
-
def redefine(method_nodes:, klass:,
|
|
16
|
+
def redefine(method_nodes:, klass:, line_numbers:, file_path:)
|
|
17
|
+
create_proxies(method_nodes:, klass:, file_path:)
|
|
18
|
+
define_methods(method_nodes:, line_numbers:)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def create_proxies(method_nodes:, klass:, file_path:)
|
|
24
|
+
method_nodes.each do |method_node|
|
|
25
|
+
name = method_node.name
|
|
26
|
+
line = FileParser.line_number(node: method_node)
|
|
27
|
+
file = FileProxy.new(path: file_path, line:, scope: "#{klass}##{name}")
|
|
28
|
+
params = param_proxies(method_node:, file:)
|
|
29
|
+
return_proxy = ProxyFactory.return_proxy(method_node:, file:)
|
|
30
|
+
|
|
31
|
+
klass.low_methods[name] = MethodProxy.new(name:, params:, return_proxy:)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def define_methods(method_nodes:, line_numbers:) # rubocop:disable Metrics
|
|
36
|
+
class_start = line_numbers[:class_start]
|
|
37
|
+
class_end = line_numbers[:class_end]
|
|
38
|
+
private_start = line_numbers[:private_start]
|
|
39
|
+
|
|
14
40
|
Module.new do
|
|
15
41
|
method_nodes.each do |method_node|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
file = FileProxy.new(path: file_path, line:, scope: "#{klass}##{method_node.name}")
|
|
19
|
-
params = Redefiner.params_with_type_expressions(method_node:, file:)
|
|
20
|
-
return_proxy = ProxyFactory.return_proxy(method_node:, file:)
|
|
42
|
+
method_start = method_node.start_line
|
|
43
|
+
next unless method_start > class_start && method_node.end_line <= class_end
|
|
21
44
|
|
|
22
|
-
|
|
45
|
+
name = method_node.name
|
|
23
46
|
|
|
24
47
|
define_method(name) do |*args, **kwargs|
|
|
25
|
-
|
|
48
|
+
method_proxy = instance_of?(Class) ? low_methods[name] : self.class.low_methods[name] || Object.low_methods[name]
|
|
49
|
+
|
|
50
|
+
method_proxy.params.each do |param_proxy|
|
|
26
51
|
# Get argument value or default value.
|
|
27
52
|
value = param_proxy.position ? args[param_proxy.position] : kwargs[param_proxy.name]
|
|
28
53
|
if value.nil? && param_proxy.type_expression.default_value != :LOW_TYPE_UNDEFINED
|
|
29
54
|
value = param_proxy.type_expression.default_value
|
|
30
55
|
end
|
|
31
|
-
|
|
56
|
+
|
|
32
57
|
param_proxy.type_expression.validate!(value:, proxy: param_proxy)
|
|
33
|
-
# Handle value(type) special case.
|
|
34
58
|
value = value.value if value.is_a?(ValueExpression)
|
|
35
|
-
# Redefine argument value.
|
|
36
59
|
param_proxy.position ? args[param_proxy.position] = value : kwargs[param_proxy.name] = value
|
|
37
60
|
end
|
|
38
61
|
|
|
39
|
-
if return_proxy
|
|
62
|
+
if (return_proxy = method_proxy.return_proxy)
|
|
40
63
|
return_value = super(*args, **kwargs)
|
|
41
|
-
return_proxy.type_expression.validate!(value: return_value, proxy:
|
|
64
|
+
return_proxy.type_expression.validate!(value: return_value, proxy: return_proxy)
|
|
42
65
|
return return_value
|
|
43
66
|
end
|
|
44
67
|
|
|
45
68
|
super(*args, **kwargs)
|
|
46
69
|
end
|
|
47
70
|
|
|
48
|
-
private name if
|
|
71
|
+
private name if private_start && method_start > private_start
|
|
49
72
|
end
|
|
50
73
|
end
|
|
51
74
|
end
|
|
52
75
|
|
|
53
|
-
def
|
|
76
|
+
def param_proxies(method_node:, file:)
|
|
54
77
|
return [] if method_node.parameters.nil?
|
|
55
78
|
|
|
56
79
|
params = method_node.parameters.slice
|
|
57
|
-
|
|
58
|
-
proxy_method = eval("-> (#{params}) {}", binding, __FILE__, __LINE__) # rubocop:disable Security/Eval
|
|
80
|
+
proxy_method = proxy_method(method_node:)
|
|
59
81
|
required_args, required_kwargs = required_args(proxy_method:)
|
|
60
82
|
|
|
61
83
|
# Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
|
|
@@ -87,7 +109,11 @@ module LowType
|
|
|
87
109
|
raise ArgumentError, "Incorrect param syntax: #{e.message}"
|
|
88
110
|
end
|
|
89
111
|
|
|
90
|
-
|
|
112
|
+
def proxy_method(method_node:)
|
|
113
|
+
params = method_node.parameters.slice
|
|
114
|
+
# Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
|
|
115
|
+
eval("-> (#{params}) {}", binding, __FILE__, __LINE__) # rubocop:disable Security/Eval
|
|
116
|
+
end
|
|
91
117
|
|
|
92
118
|
def required_args(proxy_method:)
|
|
93
119
|
required_args = []
|
|
@@ -1,42 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
# Scoped to the class that includes LowType module.
|
|
3
|
-
class Array < ::Array
|
|
4
|
-
def self.[](*types)
|
|
5
|
-
return LowType::TypeExpression.new(type: [*types]) if types.all? { |type| LowType.type?(type) }
|
|
6
|
-
super
|
|
7
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def ==(other)
|
|
15
|
-
return true if other.class == ::Array
|
|
16
|
-
super
|
|
17
|
-
end
|
|
18
|
-
end
|
|
3
|
+
module LowType
|
|
4
|
+
module Syntax
|
|
5
|
+
refine Array.singleton_class do
|
|
6
|
+
def [](*types)
|
|
7
|
+
return LowType::TypeExpression.new(type: [*types]) if types.all? { |type| LowType.type?(type) }
|
|
19
8
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def self.[](type)
|
|
23
|
-
return LowType::TypeExpression.new(type:) if LowType.type?(type)
|
|
24
|
-
super
|
|
9
|
+
super
|
|
10
|
+
end
|
|
25
11
|
end
|
|
26
12
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
13
|
+
refine Hash.singleton_class do
|
|
14
|
+
def [](type)
|
|
15
|
+
return LowType::TypeExpression.new(type:) if LowType.type?(type)
|
|
31
16
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
super
|
|
17
|
+
super
|
|
18
|
+
end
|
|
35
19
|
end
|
|
36
20
|
end
|
|
37
21
|
end
|
|
38
22
|
|
|
39
|
-
#
|
|
23
|
+
# Refine doesn't support inheritence.
|
|
40
24
|
class Object
|
|
41
25
|
# For "Type | [type_expression/type/value]" situations, redirecting to or generating a type expression from types.
|
|
42
26
|
# "|" is not defined on Object class and this is the most compute-efficient way to achieve our goal (world peace).
|
data/lib/type_expression.rb
CHANGED
|
@@ -57,9 +57,9 @@ module LowType
|
|
|
57
57
|
|
|
58
58
|
def valid_types
|
|
59
59
|
types = @types.map do |type|
|
|
60
|
-
# Remove 'LowType::'
|
|
60
|
+
# Remove 'LowType::' namespace in subtypes.
|
|
61
61
|
if type.is_a?(::Array)
|
|
62
|
-
|
|
62
|
+
"[#{type.map { |subtype| subtype.to_s.delete_prefix('LowType::') }.join(', ')}]"
|
|
63
63
|
else
|
|
64
64
|
type.inspect.to_s.delete_prefix('LowType::')
|
|
65
65
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: low_type
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -19,23 +19,23 @@ extra_rdoc_files: []
|
|
|
19
19
|
files:
|
|
20
20
|
- lib/adapters/adapter_loader.rb
|
|
21
21
|
- lib/adapters/sinatra_adapter.rb
|
|
22
|
-
- lib/basic_types.rb
|
|
23
|
-
- lib/error_types.rb
|
|
24
22
|
- lib/factories/proxy_factory.rb
|
|
23
|
+
- lib/file_parser.rb
|
|
25
24
|
- lib/instance_types.rb
|
|
26
25
|
- lib/interfaces/adapter_interface.rb
|
|
27
26
|
- lib/interfaces/error_interface.rb
|
|
28
27
|
- lib/local_types.rb
|
|
29
28
|
- lib/low_type.rb
|
|
30
|
-
- lib/parser.rb
|
|
31
29
|
- lib/proxies/file_proxy.rb
|
|
32
30
|
- lib/proxies/local_proxy.rb
|
|
33
31
|
- lib/proxies/method_proxy.rb
|
|
34
32
|
- lib/proxies/param_proxy.rb
|
|
35
33
|
- lib/proxies/return_proxy.rb
|
|
36
34
|
- lib/redefiner.rb
|
|
37
|
-
- lib/
|
|
35
|
+
- lib/syntax.rb
|
|
38
36
|
- lib/type_expression.rb
|
|
37
|
+
- lib/types/complex_types.rb
|
|
38
|
+
- lib/types/error_types.rb
|
|
39
39
|
- lib/value_expression.rb
|
|
40
40
|
- lib/version.rb
|
|
41
41
|
homepage: https://codeberg.org/low_ruby/low_type
|
|
File without changes
|