typed.rb 0.0.11

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.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +26 -0
  3. data/bin/typed.rb +110 -0
  4. data/lib/typed/language.rb +131 -0
  5. data/lib/typed/model/tm_abs.rb +104 -0
  6. data/lib/typed/model/tm_array_literal.rb +25 -0
  7. data/lib/typed/model/tm_boolean.rb +15 -0
  8. data/lib/typed/model/tm_boolean_operator.rb +34 -0
  9. data/lib/typed/model/tm_break.rb +24 -0
  10. data/lib/typed/model/tm_case_when.rb +38 -0
  11. data/lib/typed/model/tm_class.rb +63 -0
  12. data/lib/typed/model/tm_const.rb +29 -0
  13. data/lib/typed/model/tm_defined.rb +19 -0
  14. data/lib/typed/model/tm_error.rb +16 -0
  15. data/lib/typed/model/tm_float.rb +15 -0
  16. data/lib/typed/model/tm_for.rb +42 -0
  17. data/lib/typed/model/tm_fun.rb +165 -0
  18. data/lib/typed/model/tm_global_var.rb +22 -0
  19. data/lib/typed/model/tm_global_var_assignment.rb +20 -0
  20. data/lib/typed/model/tm_hash_literal.rb +32 -0
  21. data/lib/typed/model/tm_if_else.rb +24 -0
  22. data/lib/typed/model/tm_instance_var.rb +23 -0
  23. data/lib/typed/model/tm_instance_var_assignment.rb +32 -0
  24. data/lib/typed/model/tm_int.rb +15 -0
  25. data/lib/typed/model/tm_local_var_asgn.rb +35 -0
  26. data/lib/typed/model/tm_mass_asgn.rb +60 -0
  27. data/lib/typed/model/tm_mlhs.rb +87 -0
  28. data/lib/typed/model/tm_module.rb +51 -0
  29. data/lib/typed/model/tm_next.rb +24 -0
  30. data/lib/typed/model/tm_nil.rb +14 -0
  31. data/lib/typed/model/tm_range_literal.rb +30 -0
  32. data/lib/typed/model/tm_regexp.rb +27 -0
  33. data/lib/typed/model/tm_rescue.rb +27 -0
  34. data/lib/typed/model/tm_return.rb +24 -0
  35. data/lib/typed/model/tm_s_class.rb +30 -0
  36. data/lib/typed/model/tm_self.rb +22 -0
  37. data/lib/typed/model/tm_send.rb +300 -0
  38. data/lib/typed/model/tm_sequencing.rb +53 -0
  39. data/lib/typed/model/tm_string.rb +15 -0
  40. data/lib/typed/model/tm_string_interpolation.rb +21 -0
  41. data/lib/typed/model/tm_super.rb +27 -0
  42. data/lib/typed/model/tm_symbol.rb +15 -0
  43. data/lib/typed/model/tm_symbol_interpolation.rb +21 -0
  44. data/lib/typed/model/tm_try.rb +29 -0
  45. data/lib/typed/model/tm_var.rb +28 -0
  46. data/lib/typed/model/tm_while.rb +43 -0
  47. data/lib/typed/model.rb +48 -0
  48. data/lib/typed/prelude.rb +939 -0
  49. data/lib/typed/prelude_existential_registry.bin +0 -0
  50. data/lib/typed/prelude_generic_registry.bin +0 -0
  51. data/lib/typed/prelude_registry.bin +0 -0
  52. data/lib/typed/runtime/ast_parser.rb +589 -0
  53. data/lib/typed/runtime/method_signature_processor.rb +72 -0
  54. data/lib/typed/runtime/normalization/validations.rb +47 -0
  55. data/lib/typed/runtime/normalization.rb +196 -0
  56. data/lib/typed/runtime/parser_context.rb +36 -0
  57. data/lib/typed/runtime/type_parser.rb +215 -0
  58. data/lib/typed/runtime/type_registry.rb +170 -0
  59. data/lib/typed/runtime/type_signature_processor.rb +34 -0
  60. data/lib/typed/runtime.rb +33 -0
  61. data/lib/typed/type_signature/parser.rb +240 -0
  62. data/lib/typed/types/polymorphism/existential_type_variable.rb +13 -0
  63. data/lib/typed/types/polymorphism/generic_comparisons.rb +134 -0
  64. data/lib/typed/types/polymorphism/generic_variables.rb +24 -0
  65. data/lib/typed/types/polymorphism/type_variable.rb +138 -0
  66. data/lib/typed/types/polymorphism/type_variable_register.rb +298 -0
  67. data/lib/typed/types/polymorphism/unification.rb +579 -0
  68. data/lib/typed/types/ty_boolean.rb +15 -0
  69. data/lib/typed/types/ty_dynamic.rb +39 -0
  70. data/lib/typed/types/ty_either.rb +168 -0
  71. data/lib/typed/types/ty_error.rb +18 -0
  72. data/lib/typed/types/ty_existential_type.rb +22 -0
  73. data/lib/typed/types/ty_function.rb +144 -0
  74. data/lib/typed/types/ty_generic_function.rb +115 -0
  75. data/lib/typed/types/ty_generic_object.rb +180 -0
  76. data/lib/typed/types/ty_generic_singleton_object.rb +238 -0
  77. data/lib/typed/types/ty_object.rb +256 -0
  78. data/lib/typed/types/ty_singleton_object.rb +78 -0
  79. data/lib/typed/types/ty_stack_jump.rb +44 -0
  80. data/lib/typed/types/ty_top_level_object.rb +38 -0
  81. data/lib/typed/types.rb +60 -0
  82. data/lib/typed/typing_context.rb +206 -0
  83. data/lib/typed/version.rb +3 -0
  84. data/lib/typed.rb +161 -0
  85. data/spec/lib/ast_parser_spec.rb +101 -0
  86. data/spec/lib/examples/animals.rb +44 -0
  87. data/spec/lib/examples/counter.rb +16 -0
  88. data/spec/lib/examples/if.rb +31 -0
  89. data/spec/lib/language_spec.rb +36 -0
  90. data/spec/lib/model/tm_abs_spec.rb +66 -0
  91. data/spec/lib/model/tm_array_literal_spec.rb +36 -0
  92. data/spec/lib/model/tm_case_when_spec.rb +39 -0
  93. data/spec/lib/model/tm_class_spec.rb +67 -0
  94. data/spec/lib/model/tm_defined_spec.rb +10 -0
  95. data/spec/lib/model/tm_for_spec.rb +150 -0
  96. data/spec/lib/model/tm_fun_spec.rb +11 -0
  97. data/spec/lib/model/tm_hash_literal_spec.rb +40 -0
  98. data/spec/lib/model/tm_mass_asgn_spec.rb +104 -0
  99. data/spec/lib/model/tm_module_spec.rb +42 -0
  100. data/spec/lib/model/tm_regexp_spec.rb +9 -0
  101. data/spec/lib/model/tm_return_spec.rb +47 -0
  102. data/spec/lib/model/tm_s_class_spec.rb +27 -0
  103. data/spec/lib/model/tm_self_spec.rb +19 -0
  104. data/spec/lib/model/tm_string_interpolation_spec.rb +10 -0
  105. data/spec/lib/model/tm_symbol_interpolation_spec.rb +10 -0
  106. data/spec/lib/model/tm_symbol_spec.rb +9 -0
  107. data/spec/lib/model/tm_while_spec.rb +141 -0
  108. data/spec/lib/polymorphism/type_variable_spec.rb +14 -0
  109. data/spec/lib/polymorphism/unification_spec.rb +328 -0
  110. data/spec/lib/prelude/array_spec.rb +263 -0
  111. data/spec/lib/prelude/class_spec.rb +12 -0
  112. data/spec/lib/prelude/enumerable_spec.rb +278 -0
  113. data/spec/lib/prelude/enumerator_spec.rb +101 -0
  114. data/spec/lib/prelude/hash_spec.rb +361 -0
  115. data/spec/lib/prelude/kernel_spec.rb +23 -0
  116. data/spec/lib/prelude/object_spec.rb +22 -0
  117. data/spec/lib/prelude/pair_spec.rb +16 -0
  118. data/spec/lib/prelude/showable_spec.rb +31 -0
  119. data/spec/lib/prelude/string_spec.rb +98 -0
  120. data/spec/lib/runtime/normalization_spec.rb +29 -0
  121. data/spec/lib/runtime/validations_spec.rb +56 -0
  122. data/spec/lib/runtime_spec.rb +503 -0
  123. data/spec/lib/type_signature/parser_spec.rb +239 -0
  124. data/spec/lib/types/comparisons_spec.rb +35 -0
  125. data/spec/lib/types/polymorphism/generic_comparisons_spec.rb +492 -0
  126. data/spec/lib/types/polymorphism/type_variable_register_spec.rb +128 -0
  127. data/spec/lib/types/ty_dynamic_spec.rb +103 -0
  128. data/spec/lib/types/ty_either_spec.rb +288 -0
  129. data/spec/lib/types/ty_error_spec.rb +18 -0
  130. data/spec/lib/types/ty_generic_object_spec.rb +78 -0
  131. data/spec/lib/types/ty_generic_singleton_object_spec.rb +288 -0
  132. data/spec/lib/types/typing_context_spec.rb +86 -0
  133. data/spec/lib/types_spec.rb +174 -0
  134. data/spec/lib/typing/boolean_asgn_spec.rb +134 -0
  135. data/spec/lib/typing/break_spec.rb +79 -0
  136. data/spec/lib/typing/generics_spec.rb +191 -0
  137. data/spec/lib/typing/instance_vars_spec.rb +103 -0
  138. data/spec/lib/typing/next_spec.rb +29 -0
  139. data/spec/lib/typing/op_asgn_spec.rb +104 -0
  140. data/spec/lib/typing/overriden_methods_spec.rb +31 -0
  141. data/spec/lib/typing/subtyping_spec.rb +112 -0
  142. data/spec/lib/typing/tm_boolean_operator_spec.rb +100 -0
  143. data/spec/lib/typing/tm_boolean_spec.rb +61 -0
  144. data/spec/lib/typing/tm_const_spec.rb +28 -0
  145. data/spec/lib/typing/tm_defined_spec.rb +12 -0
  146. data/spec/lib/typing/tm_fun_spec.rb +347 -0
  147. data/spec/lib/typing/tm_global_var_spec.rb +33 -0
  148. data/spec/lib/typing/tm_if_else_spec.rb +104 -0
  149. data/spec/lib/typing/tm_ignore_spec.rb +24 -0
  150. data/spec/lib/typing/tm_instance_vars_spec.rb +117 -0
  151. data/spec/lib/typing/tm_local_var_asgn_spec.rb +134 -0
  152. data/spec/lib/typing/tm_mlhs_spec.rb +164 -0
  153. data/spec/lib/typing/tm_module_spec.rb +89 -0
  154. data/spec/lib/typing/tm_raise_spec.rb +31 -0
  155. data/spec/lib/typing/tm_range_literal_spec.rb +25 -0
  156. data/spec/lib/typing/tm_regexp_spec.rb +14 -0
  157. data/spec/lib/typing/tm_return_spec.rb +45 -0
  158. data/spec/lib/typing/tm_send_casting_spec.rb +26 -0
  159. data/spec/lib/typing/tm_send_class_methods_spec.rb +42 -0
  160. data/spec/lib/typing/tm_send_generic_apply_spec.rb +103 -0
  161. data/spec/lib/typing/tm_send_generic_methods_spec.rb +77 -0
  162. data/spec/lib/typing/tm_send_initialize_spec.rb +68 -0
  163. data/spec/lib/typing/tm_send_lambda_spec.rb +135 -0
  164. data/spec/lib/typing/tm_send_spec.rb +217 -0
  165. data/spec/lib/typing/tm_send_yield_block_spec.rb +308 -0
  166. data/spec/lib/typing/tm_sequencing_spec.rb +174 -0
  167. data/spec/lib/typing/tm_string_interpolation_spec.rb +19 -0
  168. data/spec/lib/typing/tm_super_spec.rb +63 -0
  169. data/spec/lib/typing/tm_symbol_interpolation_spec.rb +19 -0
  170. data/spec/lib/typing/tm_symbol_spec.rb +14 -0
  171. data/spec/lib/typing/tm_try_spec.rb +73 -0
  172. data/spec/spec_helper.rb +140 -0
  173. metadata +216 -0
@@ -0,0 +1,60 @@
1
+ module TypedRb
2
+ class TypeCheckError < TypeError
3
+ attr_reader :node
4
+
5
+ def initialize(msg, node = nil)
6
+ super(build_message_error(msg, node))
7
+ @node = node
8
+ end
9
+
10
+ private
11
+
12
+ def build_message_error(msg, nodes)
13
+ file = $TYPECHECK_FILE || 'NO FILE'
14
+ if nodes && nodes.is_a?(Array)
15
+ num_columns = (nodes.last.loc.column - 2)
16
+ num_columns = num_columns < 0 ? 0 : num_columns
17
+ "\n #{msg}\n...\n>>>#{file}:#{nodes.first.loc.line}\n#{'=' * (nodes.first.loc.column - 2)}> #{nodes.first.loc.expression.source}\n\
18
+ ##{file}:#{nodes.last.loc.line}\n#{'=' * num_columns}> #{nodes.last.loc.expression.source}\n...\n"
19
+ elsif nodes
20
+ line = nodes.loc.line
21
+ num_columns = (nodes.loc.column - 2)
22
+ num_columns = num_columns < 0 ? 0 : num_columns
23
+
24
+ "\n>>>#{file}:#{line}\n #{msg}\n...\n#{'=' * num_columns}> #{nodes.loc.expression.source}\n...\n"
25
+ else
26
+ msg
27
+ end
28
+ end
29
+ end
30
+
31
+ module Types
32
+ class TypeParsingError < TypeCheckError; end
33
+
34
+ class Type
35
+ attr_accessor :node
36
+
37
+ def initialize(node)
38
+ @node = node
39
+ end
40
+
41
+ def stack_jump?
42
+ false
43
+ end
44
+
45
+ def either?
46
+ false
47
+ end
48
+
49
+ # other_type is a meta-type not a ruby type
50
+ def compatible?(other_type, relation = :lt)
51
+ if other_type.instance_of?(Class)
52
+ self.instance_of?(other_type) || other_type == TyError
53
+ else
54
+ relation = (relation == :lt ? :gt : lt)
55
+ other_type.instance_of?(self.class, relation) || other_type.instance_of?(TyError)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,206 @@
1
+ module TypedRb
2
+ module Types
3
+ class TypingContext
4
+ class << self
5
+ def namespace
6
+ @namespace ||= []
7
+ end
8
+
9
+ def namespace_push(constant)
10
+ parts = constant.split('::')
11
+ @namespace += parts.reject { |part| namespace.include?(part) }
12
+ end
13
+
14
+ def namespace_pop
15
+ @namespace.pop
16
+ end
17
+
18
+ def find_namespace(constant, namespace = self.namespace)
19
+ return Class.for_name(constant) if constant.start_with?('::')
20
+ Class.for_name(namespace.join('::') + '::' + constant)
21
+ rescue NameError => e
22
+ if namespace.empty?
23
+ raise e
24
+ else
25
+ find_namespace(constant, namespace.take(namespace.size - 1))
26
+ end
27
+ end
28
+
29
+ def function_context_push(type, message, args)
30
+ @function_context = [type, message, args]
31
+ end
32
+
33
+ attr_reader :function_context
34
+
35
+ def function_context_pop
36
+ @function_context = nil
37
+ end
38
+
39
+ def empty_typing_context
40
+ Polymorphism::TypeVariableRegister.new(nil, :local)
41
+ end
42
+
43
+ def type_variables_register
44
+ @type_variables_register ||= Polymorphism::TypeVariableRegister.new(nil, :top_level)
45
+ end
46
+
47
+ def type_variable_for(type, variable, hierarchy)
48
+ type_variables_register.type_variable_for(type, variable, hierarchy)
49
+ end
50
+
51
+ def type_variable_for_global(variable)
52
+ type_variables_register.type_variable_for_global(variable)
53
+ end
54
+
55
+ def type_variable_for_message(variable, message)
56
+ type_variables_register.type_variable_for_message(variable, message)
57
+ end
58
+
59
+ def type_variable_for_abstraction(abs_kind, variable, context)
60
+ type_variables_register.type_variable_for_abstraction(abs_kind, variable, context)
61
+ end
62
+
63
+ def type_variable_for_function_type(type_var)
64
+ type_variables_register.type_variable_for_generic_type(type_var, true)
65
+ { :lt => :upper_bound, :gt => :lower_bound }.each do |relation, bound|
66
+ if type_var.send(bound)
67
+ value = if type_var.send(bound).is_a?(TyGenericSingletonObject)
68
+ type_var.send(bound).clone
69
+ else
70
+ type_var.send(bound)
71
+ end
72
+ type_var.compatible?(value, relation)
73
+ end
74
+ end
75
+ end
76
+
77
+ def type_variable_for_generic_type(type_var)
78
+ type_variables_register.type_variable_for_generic_type(type_var)
79
+ end
80
+
81
+ def local_type_variable
82
+ type_variables_register.local_type_variable
83
+ end
84
+
85
+ def all_constraints
86
+ type_variables_register.all_constraints
87
+ end
88
+
89
+ def all_variables
90
+ type_variables_register.all_variables
91
+ end
92
+
93
+ def include?(variable)
94
+ type_variables_register.include?(variable)
95
+ end
96
+
97
+ def add_constraint(variable, relation, type)
98
+ type_variables_register.add_constraint(variable, relation, type)
99
+ end
100
+
101
+ def constraints_for(variable)
102
+ type_variables_register.constraints[variable] || []
103
+ end
104
+
105
+ def duplicate(within_context)
106
+ current_parent = type_variables_register.parent
107
+ type_variables_register.parent = nil
108
+ duplicated = Marshal.load(Marshal.dump(within_context))
109
+ type_variables_register.parent = current_parent
110
+ duplicated
111
+ end
112
+
113
+ def bound_generic_type_var?(type_variable)
114
+ type_variables_register.bound_generic_type_var?(type_variable)
115
+ end
116
+
117
+ def push_context(type)
118
+ new_register = Polymorphism::TypeVariableRegister.new(type_variables_register, type)
119
+ @type_variables_register.children << new_register
120
+ @type_variables_register = new_register
121
+ new_register
122
+ end
123
+
124
+ def pop_context
125
+ fail StandardError, 'Empty typing context stack, impossible to pop' if @type_variables_register.nil?
126
+ last_register = type_variables_register
127
+ @type_variables_register = @type_variables_register.parent
128
+ @type_variables_register.children.reject! { |child| child == last_register }
129
+ last_register
130
+ end
131
+
132
+ def with_context(context)
133
+ old_context = @type_variables_register
134
+ @type_variables_register = context
135
+ result = yield
136
+ @type_variables_register = old_context
137
+ result
138
+ end
139
+
140
+ def clear(type)
141
+ @type_variables_register = Polymorphism::TypeVariableRegister.new(type)
142
+ end
143
+
144
+ def vars_info(level)
145
+ method_registry = type_variables_register
146
+ while !method_registry.nil? && method_registry.kind != level
147
+ method_registry = method_registry.parent
148
+ end
149
+
150
+ if method_registry
151
+ method_registry.type_variables_register.map do |(key, type_var)|
152
+ type_var if key.first == :generic
153
+ end.compact.each_with_object({}) do |type_var, acc|
154
+ var_name = type_var.variable.split(':').last
155
+ acc["[#{var_name}]"] = type_var
156
+ end
157
+ else
158
+ {}
159
+ end
160
+ end
161
+ end
162
+
163
+ # work with types
164
+ def self.top_level
165
+ TypingContext.new.add_binding!(:self, TyTopLevelObject.new)
166
+ end
167
+
168
+ def initialize(parent = nil)
169
+ @parent = parent
170
+ @bindings = {}
171
+ end
172
+
173
+ def add_binding(val, type)
174
+ TypingContext.new(self).push_binding(val, type)
175
+ end
176
+
177
+ def add_binding!(val, type)
178
+ push_binding(val, type)
179
+ end
180
+
181
+ def get_type_for(val)
182
+ type = @bindings[val.to_s]
183
+ if type.nil?
184
+ @parent.get_type_for(val) if @parent
185
+ else
186
+ type
187
+ end
188
+ end
189
+
190
+ def get_self
191
+ get_type_for('self')
192
+ end
193
+
194
+ def context_name
195
+ get_self.to_s
196
+ end
197
+
198
+ protected
199
+
200
+ def push_binding(val, type)
201
+ @bindings[val.to_s] = type
202
+ self
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,3 @@
1
+ module TypedRb
2
+ VERSION = '0.0.11'
3
+ end
data/lib/typed.rb ADDED
@@ -0,0 +1,161 @@
1
+ require 'log4r'
2
+ class Class
3
+ def for_name(klass)
4
+ return TrueClass if klass == 'Boolean'
5
+ return NilClass if klass == 'unit'
6
+ const_get(klass)
7
+ end
8
+ end
9
+
10
+ module Kernel
11
+ alias_method :old_require, :require
12
+ alias_method :old_load, :load
13
+ alias_method :old_require_relative, :require_relative
14
+
15
+ def load(name, wrap = false)
16
+ if $LOAD_TO_TYPECHECK
17
+ return if $LOADED_MAP[name]
18
+ to_load = if File.exist?(name)
19
+ File.absolute_path(name)
20
+ else
21
+ dir = $LOAD_PATH.detect do |d|
22
+ File.exist?(File.join(d, name))
23
+ end
24
+ return if dir.nil?
25
+ File.absolute_path(File.join(dir, name))
26
+ end
27
+ if $LOADED_MAP[to_load].nil?
28
+ #puts "** LOADING #{to_load}"
29
+ process_dependency(to_load) { old_load(name, wrap) }
30
+ else
31
+ old_load(name, wrap)
32
+ end
33
+ else
34
+ old_load(name, wrap)
35
+ end
36
+ end
37
+
38
+ def require(name)
39
+ if $LOAD_TO_TYPECHECK
40
+ dependency = ["#{name}.rb", name].detect { |f| File.exist?(f) }
41
+ if dependency.nil?
42
+ # system dependency
43
+ old_require(name)
44
+ else
45
+ to_load = File.absolute_path(dependency)
46
+ if $LOADED_MAP[to_load].nil?
47
+ # puts "** REQUIRING #{to_load}"
48
+ process_dependency(to_load) { old_require(name) }
49
+ else
50
+ old_require(name)
51
+ end
52
+ end
53
+ else
54
+ old_require(name)
55
+ end
56
+ end
57
+
58
+ def require_relative(name)
59
+ dirs = caller.map do |call|
60
+ file = call.split(':').first
61
+ File.dirname(file)
62
+ end.uniq
63
+ found = dirs.map do |dir|
64
+ File.join(dir, name)
65
+ end.detect do |potential_file|
66
+ (File.exist?(potential_file + '.rb') || File.exist?(potential_file + '.rb'))
67
+ end
68
+ require(found)
69
+ end
70
+
71
+ def self.reset_dependencies
72
+ $FILES_TO_TYPECHECK = {}
73
+ $CURRENT_DEPS = $FILES_TO_TYPECHECK
74
+ $LOADED_MAP = {}
75
+ end
76
+
77
+ def self.with_dependency_tracking
78
+ $LOAD_TO_TYPECHECK = true
79
+ yield
80
+ $LOAD_TO_TYPECHECK = false
81
+ end
82
+
83
+ def self.computed_dependencies(acc = [], graph = $FILES_TO_TYPECHECK)
84
+ graph.each do |(file, deps)|
85
+ acc = computed_dependencies(acc, deps) if deps != {}
86
+ acc << file
87
+ end
88
+ acc
89
+ end
90
+
91
+ def process_dependency(to_load)
92
+ $LOADED_MAP[to_load] = true
93
+ $CURRENT_DEPS[to_load] = {}
94
+ old_current_deps = $CURRENT_DEPS
95
+ $CURRENT_DEPS = $CURRENT_DEPS[to_load]
96
+ yield to_load
97
+ $CURRENT_DEPS = old_current_deps
98
+ end
99
+ end
100
+ Kernel.reset_dependencies
101
+
102
+ module TypedRb
103
+ def log(client_binding, level, message)
104
+ client = client_binding.receiver
105
+ client_id = if client.instance_of?(Class)
106
+ if client.name
107
+ client.name
108
+ else
109
+ Class.for_name(client.to_s.match(/Class:(.*)>/)[1]).name
110
+ end
111
+ else
112
+ if client.class.name
113
+ client.class.name
114
+ else
115
+ Class.for_name(client.class.to_s.match(/Class:(.*)>/)[1]).name
116
+ end
117
+ end
118
+ line = client_binding.eval('__LINE__')
119
+ file = client_binding.eval('__FILE__')
120
+ message = "#{file}:#{line}\n #{message}\n"
121
+ logger('[' + client_id.gsub('::', '/') + ']').send(level, message)
122
+ end
123
+
124
+ def logger(client)
125
+ logger = Log4r::Logger[client]
126
+ logger = Log4r::Logger.new(client) if logger.nil?
127
+ logger.outputters = Log4r::Outputter.stdout
128
+ set_level(logger)
129
+ logger
130
+ end
131
+
132
+ def set_level(logger)
133
+ logger.level = case (ENV['LOG_LEVEL'] || ENV['log_level'] || '').upcase
134
+ when 'DEBUG'
135
+ Log4r::DEBUG
136
+ when 'INFO'
137
+ Log4r::INFO
138
+ when 'WARN'
139
+ Log4r::WARN
140
+ when 'ERROR'
141
+ Log4r::ERROR
142
+ when 'FATAL'
143
+ Log4r::FATAL
144
+ else
145
+ Log4r::INFO
146
+ end
147
+ end
148
+ end
149
+
150
+ TypedRb.module_eval do
151
+ module_function(:log)
152
+ public :log
153
+ module_function(:logger)
154
+ public :logger
155
+ module_function(:set_level)
156
+ public :set_level
157
+ end
158
+
159
+ Dir[File.join(File.dirname(__FILE__), '**/*.rb')].each do |file|
160
+ load(file) if file != __FILE__ && !file.end_with?('lib/typed/prelude.rb')
161
+ end
@@ -0,0 +1,101 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe TypedRb::AstParser do
4
+ subject { described_class.new }
5
+
6
+ context '#ast' do
7
+ let(:ruby_code) { 'def f(a,b); a + b; end' }
8
+ let(:parser) { Object.new.extend(described_class) }
9
+ it 'parses ruby code and return an AST structure' do
10
+ parsed = described_class.new.ast(ruby_code)
11
+ expect(parsed).to be_instance_of(Parser::AST::Node)
12
+ end
13
+ end
14
+
15
+ context '#parse' do
16
+ it 'should parse a class statement' do
17
+ parsed = parse('class A < B; 1; end')
18
+ expect(parsed).to be_instance_of(TypedRb::Model::TmClass)
19
+ expect(parsed.class_name).to be == 'A'
20
+ expect(parsed.super_class_name).to be == 'B'
21
+ expect(parsed.body).to be_instance_of(TypedRb::Model::TmInt)
22
+ end
23
+
24
+ it 'should parse classes without explicit superclass' do
25
+ parsed = parse('class A; 1; end')
26
+ expect(parsed).to be_instance_of(TypedRb::Model::TmClass)
27
+ expect(parsed.class_name).to be == 'A'
28
+ expect(parsed.super_class_name).to be == 'Object'
29
+ end
30
+
31
+ it 'should parse classes without body' do
32
+ parsed = parse('class A; end')
33
+ expect(parsed).to be_instance_of(TypedRb::Model::TmClass)
34
+ expect(parsed.class_name).to be == 'A'
35
+ expect(parsed.super_class_name).to be == 'Object'
36
+ end
37
+
38
+ it 'should parse classes with namespaces' do
39
+ parsed = parse('class Ma::Mb::A < Mc::B; 1; end')
40
+ expect(parsed).to be_instance_of(TypedRb::Model::TmClass)
41
+ expect(parsed.class_name).to be == 'Ma::Mb::A'
42
+ expect(parsed.super_class_name).to be == 'Mc::B'
43
+ end
44
+
45
+ it 'should parse definition of functions' do
46
+ parsed = parse('def f(x); x; end')
47
+ expect(parsed).to be_instance_of(TypedRb::Model::TmFun)
48
+ expect(parsed.name).to be == :f
49
+ expect(parsed.args.size).to be == 1
50
+ expect(parsed.args.first.first).to be == :arg
51
+ expect(parsed.body.to_s).to be == 'x'
52
+ expect(parsed.owner).to be_nil
53
+ end
54
+
55
+ it 'should parse definition of functions with optional args' do
56
+ parsed = parse('def f(x, y=2); x; end')
57
+ expect(parsed).to be_instance_of(TypedRb::Model::TmFun)
58
+ expect(parsed.name).to be == :f
59
+ expect(parsed.args.size).to be == 2
60
+ expect(parsed.args.first.first).to be == :arg
61
+ expect(parsed.args[1].first).to be == :optarg
62
+ expect(parsed.args[1].last).to be_instance_of(TypedRb::Model::TmInt)
63
+ expect(parsed.body.to_s).to be == 'x'
64
+ expect(parsed.owner).to be_nil
65
+ end
66
+
67
+ it 'should parse definition of functions with block args' do
68
+ parsed = parse('def f(x, &b); x; end')
69
+ expect(parsed).to be_instance_of(TypedRb::Model::TmFun)
70
+ expect(parsed.name).to be == :f
71
+ expect(parsed.args.size).to be == 2
72
+ expect(parsed.args.first.first).to be == :arg
73
+ expect(parsed.args[1].first).to be == :blockarg
74
+ expect(parsed.body.to_s).to be == 'x'
75
+ expect(parsed.owner).to be_nil
76
+ end
77
+
78
+ it 'should parse definition of self functions' do
79
+ parsed = parse('def self.f(x); x; end')
80
+ expect(parsed).to be_instance_of(TypedRb::Model::TmFun)
81
+ expect(parsed.name).to be == :f
82
+ expect(parsed.args.size).to be == 1
83
+ expect(parsed.args.first.first).to be == :arg
84
+ expect(parsed.owner).to be == :self
85
+ end
86
+
87
+ it 'should parse the use of instance variables' do
88
+ parsed = parse('@a')
89
+ expect(parsed).to be_instance_of(TypedRb::Model::TmInstanceVar)
90
+ expect(parsed.val).to be == :@a
91
+ end
92
+
93
+ it 'should parse instance variable assginations' do
94
+ parsed = parse('@a = 3')
95
+ expect(parsed).to be_instance_of(TypedRb::Model::TmInstanceVarAssignment)
96
+ expect(parsed.lvalue).to be_instance_of(TypedRb::Model::TmInstanceVar)
97
+ expect(parsed.lvalue.val).to be == :@a
98
+ expect(parsed.rvalue).to be_instance_of(TypedRb::Model::TmInt)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,44 @@
1
+ class Animal1
2
+
3
+ ts '#make_sound / -> String'
4
+ def make_sound
5
+ 'animal sound'
6
+ end
7
+
8
+ end
9
+
10
+ class Cat1 < Animal1
11
+
12
+ ts '#make_sound / -> String'
13
+ def make_sound
14
+ "meow"
15
+ end
16
+
17
+ ts '#jump / -> unit'
18
+ def jump; end
19
+ end
20
+
21
+
22
+ class Dog1 < Animal1
23
+
24
+ ts '#make_sound / -> String'
25
+ def make_sound
26
+ "bark"
27
+ end
28
+
29
+ end
30
+
31
+ ts '#mindless_func / Array[Animal1] -> Array[Animal1]'
32
+ def mindless_func(xs)
33
+ xs.push(Dog1.new)
34
+ end
35
+
36
+ cats = Array.(Cat1).new
37
+ cats.push(Cat1.new)
38
+
39
+ # Type error should be raised here
40
+ horror = mindless_func(cats)
41
+
42
+ lambda {
43
+ horror.last.jump
44
+ }
@@ -0,0 +1,16 @@
1
+ class Counter
2
+ ts '#initialize / Integer -> unit'
3
+ def initialize(start_value)
4
+ @counter = start_value
5
+ end
6
+
7
+ ts '#inc / Integer -> Integer'
8
+ def inc(num=1)
9
+ @counter = @counter + num
10
+ end
11
+
12
+ ts '#counter / -> Integer'
13
+ def counter
14
+ @counter
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ class TestIf
2
+
3
+ ts '#initialize / -> unit'
4
+ def initialize
5
+ @state = 'unknown'
6
+ end
7
+
8
+ ts '#open! / -> String'
9
+ def open!
10
+ @state = 'open'
11
+ end
12
+
13
+ ts '#close / -> String'
14
+ def close
15
+ @state = 'close'
16
+ end
17
+
18
+ ts '#set_state / String -> String'
19
+ def set_state(state)
20
+ @state = state
21
+ end
22
+
23
+ ts '#open / -> String'
24
+ def open
25
+ if @state == 'open'
26
+ @state
27
+ else
28
+ 'not_open'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe TypedRb::Language do
4
+ let(:language) { described_class.new }
5
+ let(:file) { File.join(File.dirname(__FILE__), 'examples', example) }
6
+
7
+ context 'with valid source code' do
8
+
9
+ let(:example) { 'counter.rb' }
10
+
11
+ it 'should be possible to type check the code' do
12
+ language.check_file(file)
13
+ expect_binding(language, Counter, '@counter', Integer)
14
+ end
15
+ end
16
+
17
+ context 'with valid source code including conditionals' do
18
+ let(:example) { 'if.rb' }
19
+
20
+ it 'should be possible to type check the code' do
21
+ language.check_file(file)
22
+ expect_binding(language, TestIf, '@state', String)
23
+ end
24
+ end
25
+
26
+ context 'with valid source code generic arrays' do
27
+ let(:example) { 'animals.rb' }
28
+
29
+ it 'should be possible to type check errors about array invariance' do
30
+ expect {
31
+ language.check_file(file)
32
+ }.to raise_error(TypedRb::TypeCheckError,
33
+ /Error type checking message sent 'mindless_func': Array\[Animal1\] expected, Array\[Cat1\] found/)
34
+ end
35
+ end
36
+ end