puppet 4.3.2 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (487) hide show
  1. checksums.yaml +4 -4
  2. data/COMMITTERS.md +2 -2
  3. data/CONTRIBUTING.md +6 -6
  4. data/LICENSE +1 -1
  5. data/README.md +8 -9
  6. data/conf/auth.conf +2 -2
  7. data/ext/README.environment +1 -1
  8. data/ext/debian/README.source +1 -1
  9. data/ext/debian/control +1 -1
  10. data/ext/debian/copyright +4 -4
  11. data/ext/debian/puppetmaster.README.debian +11 -9
  12. data/ext/emacs/puppet-mode.el +1 -1
  13. data/ext/envpuppet +2 -2
  14. data/ext/ips/puppetagent.xml +1 -1
  15. data/ext/ips/puppetmaster.xml +1 -1
  16. data/ext/project_data.yaml +8 -0
  17. data/ext/puppet-test +3 -3
  18. data/ext/rack/example-passenger-vhost.conf +1 -1
  19. data/ext/redhat/puppet.spec.erb +2 -2
  20. data/ext/regexp_nodes/regexp_nodes.rb +1 -1
  21. data/ext/solaris/pkginfo +1 -1
  22. data/ext/solaris/smf/puppet.xml +1 -1
  23. data/ext/suse/puppet.spec +2 -2
  24. data/ext/upload_facts.rb +1 -1
  25. data/ext/windows/puppet_interactive.bat +6 -0
  26. data/ext/windows/puppet_shell.bat +9 -0
  27. data/ext/windows/run_puppet_interactive.bat +9 -0
  28. data/ext/yaml_nodes.rb +1 -1
  29. data/install.rb +30 -20
  30. data/lib/puppet/agent.rb +1 -1
  31. data/lib/puppet/application/agent.rb +4 -2
  32. data/lib/puppet/application/apply.rb +7 -4
  33. data/lib/puppet/application/cert.rb +1 -1
  34. data/lib/puppet/application/device.rb +1 -1
  35. data/lib/puppet/application/filebucket.rb +1 -1
  36. data/lib/puppet/application/inspect.rb +1 -1
  37. data/lib/puppet/application/lookup.rb +4 -4
  38. data/lib/puppet/application/master.rb +2 -2
  39. data/lib/puppet/application/resource.rb +1 -1
  40. data/lib/puppet/configurer.rb +100 -22
  41. data/lib/puppet/data_providers/hiera_config.rb +28 -3
  42. data/lib/puppet/data_providers/hiera_interpolate.rb +30 -15
  43. data/lib/puppet/data_providers/hiera_support.rb +1 -1
  44. data/lib/puppet/data_providers/json_data_provider_factory.rb +2 -2
  45. data/lib/puppet/data_providers/yaml_data_provider_factory.rb +2 -2
  46. data/lib/puppet/defaults.rb +65 -19
  47. data/lib/puppet/environments.rb +3 -1
  48. data/lib/puppet/face/config.rb +1 -1
  49. data/lib/puppet/face/epp.rb +1 -1
  50. data/lib/puppet/face/help/man.erb +1 -1
  51. data/lib/puppet/face/module/install.rb +6 -6
  52. data/lib/puppet/face/parser.rb +12 -9
  53. data/lib/puppet/face/status.rb +2 -1
  54. data/lib/puppet/feature/cfpropertylist.rb +3 -0
  55. data/lib/puppet/feature/telnet.rb +9 -0
  56. data/lib/puppet/file_serving/http_metadata.rb +46 -0
  57. data/lib/puppet/file_serving/metadata.rb +18 -2
  58. data/lib/puppet/file_serving/terminus_selector.rb +2 -0
  59. data/lib/puppet/file_system.rb +2 -2
  60. data/lib/puppet/file_system/file_impl.rb +2 -2
  61. data/lib/puppet/file_system/memory_impl.rb +1 -1
  62. data/lib/puppet/file_system/uniquefile.rb +1 -1
  63. data/lib/puppet/forge.rb +1 -1
  64. data/lib/puppet/forge/repository.rb +1 -31
  65. data/lib/puppet/functions.rb +45 -6
  66. data/lib/puppet/functions/assert_type.rb +9 -9
  67. data/lib/puppet/functions/each.rb +5 -13
  68. data/lib/puppet/functions/filter.rb +5 -14
  69. data/lib/puppet/functions/map.rb +6 -14
  70. data/lib/puppet/functions/reduce.rb +5 -13
  71. data/lib/puppet/functions/reverse_each.rb +82 -0
  72. data/lib/puppet/functions/scanf.rb +15 -18
  73. data/lib/puppet/functions/slice.rb +22 -36
  74. data/lib/puppet/functions/split.rb +2 -2
  75. data/lib/puppet/functions/step.rb +88 -0
  76. data/lib/puppet/functions/type.rb +70 -0
  77. data/lib/puppet/graph/rb_tree_map.rb +1 -1
  78. data/lib/puppet/indirector/catalog/compiler.rb +188 -5
  79. data/lib/puppet/indirector/file_content/http.rb +15 -0
  80. data/lib/puppet/indirector/file_metadata/http.rb +27 -0
  81. data/lib/puppet/indirector/generic_http.rb +16 -0
  82. data/lib/puppet/indirector/node/exec.rb +1 -1
  83. data/lib/puppet/indirector/node/ldap.rb +1 -1
  84. data/lib/puppet/indirector/rest.rb +2 -1
  85. data/lib/puppet/info_service/class_information_service.rb +13 -12
  86. data/lib/puppet/loaders.rb +1 -0
  87. data/lib/puppet/module.rb +3 -0
  88. data/lib/puppet/module_tool/skeleton/templates/generator/Gemfile +9 -2
  89. data/lib/puppet/module_tool/skeleton/templates/generator/spec/classes/init_spec.rb.erb +1 -1
  90. data/lib/puppet/module_tool/skeleton/templates/generator/tests/init.pp.erb +2 -2
  91. data/lib/puppet/module_tool/tar/mini.rb +3 -3
  92. data/lib/puppet/network/http/pool.rb +9 -0
  93. data/lib/puppet/node.rb +1 -1
  94. data/lib/puppet/node/environment.rb +11 -2
  95. data/lib/puppet/parser/ast/pops_bridge.rb +19 -22
  96. data/lib/puppet/parser/compiler.rb +3 -3
  97. data/lib/puppet/parser/environment_compiler.rb +0 -1
  98. data/lib/puppet/parser/functions.rb +28 -16
  99. data/lib/puppet/parser/functions/fqdn_rand.rb +1 -1
  100. data/lib/puppet/parser/functions/inline_template.rb +1 -1
  101. data/lib/puppet/parser/functions/map.rb +1 -1
  102. data/lib/puppet/parser/functions/scanf.rb +15 -26
  103. data/lib/puppet/parser/functions/slice.rb +17 -24
  104. data/lib/puppet/parser/functions/split.rb +1 -1
  105. data/lib/puppet/parser/resource.rb +19 -17
  106. data/lib/puppet/parser/scope.rb +176 -5
  107. data/lib/puppet/plugins/data_providers/data_provider.rb +54 -13
  108. data/lib/puppet/pops.rb +0 -8
  109. data/lib/puppet/pops/adaptable.rb +4 -1
  110. data/lib/puppet/pops/adapters.rb +38 -13
  111. data/lib/puppet/pops/binder/binder.rb +21 -17
  112. data/lib/puppet/pops/binder/binder_issues.rb +8 -6
  113. data/lib/puppet/pops/binder/bindings_checker.rb +12 -8
  114. data/lib/puppet/pops/binder/bindings_composer.rb +16 -12
  115. data/lib/puppet/pops/binder/bindings_factory.rb +108 -104
  116. data/lib/puppet/pops/binder/bindings_model.rb +49 -47
  117. data/lib/puppet/pops/binder/config/diagnostic_producer.rb +10 -6
  118. data/lib/puppet/pops/binder/injector.rb +53 -48
  119. data/lib/puppet/pops/binder/key_factory.rb +10 -6
  120. data/lib/puppet/pops/binder/producers.rb +67 -62
  121. data/lib/puppet/pops/evaluator/access_operator.rb +95 -93
  122. data/lib/puppet/pops/evaluator/closure.rb +84 -68
  123. data/lib/puppet/pops/evaluator/collector_transformer.rb +18 -14
  124. data/lib/puppet/pops/evaluator/collectors/exported_collector.rb +0 -1
  125. data/lib/puppet/pops/evaluator/compare_operator.rb +13 -9
  126. data/lib/puppet/pops/evaluator/epp_evaluator.rb +9 -8
  127. data/lib/puppet/pops/evaluator/evaluator_impl.rb +78 -76
  128. data/lib/puppet/pops/evaluator/json_strict_literal_evaluator.rb +85 -0
  129. data/lib/puppet/pops/evaluator/relationship_operator.rb +13 -11
  130. data/lib/puppet/pops/evaluator/runtime3_converter.rb +5 -0
  131. data/lib/puppet/pops/evaluator/runtime3_support.rb +41 -45
  132. data/lib/puppet/pops/issue_reporter.rb +6 -4
  133. data/lib/puppet/pops/issues.rb +34 -11
  134. data/lib/puppet/pops/loader/base_loader.rb +1 -1
  135. data/lib/puppet/pops/loader/loader.rb +1 -1
  136. data/lib/puppet/pops/loader/loader_paths.rb +15 -0
  137. data/lib/puppet/pops/loader/module_loaders.rb +17 -13
  138. data/lib/puppet/pops/loader/puppet_function_instantiator.rb +16 -12
  139. data/lib/puppet/pops/loader/ruby_function_instantiator.rb +16 -3
  140. data/lib/puppet/pops/loader/type_definition_instantiator.rb +55 -0
  141. data/lib/puppet/pops/loaders.rb +51 -9
  142. data/lib/puppet/pops/lookup.rb +14 -12
  143. data/lib/puppet/pops/merge_strategy.rb +16 -19
  144. data/lib/puppet/pops/model/factory.rb +26 -2
  145. data/lib/puppet/pops/model/model.rb +8 -8
  146. data/lib/puppet/pops/model/model_label_provider.rb +13 -7
  147. data/lib/puppet/pops/model/model_meta.rb +17 -0
  148. data/lib/puppet/pops/model/model_tree_dumper.rb +8 -0
  149. data/lib/puppet/pops/parser/egrammar.ra +38 -14
  150. data/lib/puppet/pops/parser/eparser.rb +1353 -1276
  151. data/lib/puppet/pops/parser/epp_support.rb +11 -7
  152. data/lib/puppet/pops/parser/evaluating_parser.rb +14 -10
  153. data/lib/puppet/pops/parser/heredoc_support.rb +15 -11
  154. data/lib/puppet/pops/parser/lexer2.rb +26 -19
  155. data/lib/puppet/pops/parser/lexer_support.rb +85 -7
  156. data/lib/puppet/pops/parser/locator.rb +21 -0
  157. data/lib/puppet/pops/parser/parser_support.rb +19 -16
  158. data/lib/puppet/pops/parser/slurp_support.rb +11 -7
  159. data/lib/puppet/pops/types/class_loader.rb +23 -19
  160. data/lib/puppet/pops/types/enumeration.rb +9 -26
  161. data/lib/puppet/pops/types/iterable.rb +308 -0
  162. data/lib/puppet/pops/types/recursion_guard.rb +82 -0
  163. data/lib/puppet/pops/types/type_acceptor.rb +25 -0
  164. data/lib/puppet/pops/types/type_asserter.rb +10 -9
  165. data/lib/puppet/pops/types/type_calculator.rb +138 -381
  166. data/lib/puppet/pops/types/type_factory.rb +91 -57
  167. data/lib/puppet/pops/types/type_formatter.rb +334 -0
  168. data/lib/puppet/pops/types/type_mismatch_describer.rb +226 -59
  169. data/lib/puppet/pops/types/type_parser.rb +159 -112
  170. data/lib/puppet/pops/types/types.rb +2057 -1247
  171. data/lib/puppet/pops/utils.rb +11 -10
  172. data/lib/puppet/pops/validation.rb +11 -9
  173. data/lib/puppet/pops/validation/checker4_0.rb +83 -55
  174. data/lib/puppet/pops/validation/validator_factory_4_0.rb +8 -4
  175. data/lib/puppet/provider/aixobject.rb +1 -1
  176. data/lib/puppet/provider/augeas/augeas.rb +1 -1
  177. data/lib/puppet/provider/cron/crontab.rb +1 -1
  178. data/lib/puppet/provider/exec/windows.rb +1 -1
  179. data/lib/puppet/provider/macauthorization/macauthorization.rb +10 -9
  180. data/lib/puppet/provider/nameservice/directoryservice.rb +35 -50
  181. data/lib/puppet/provider/package/appdmg.rb +3 -2
  182. data/lib/puppet/provider/package/dnf.rb +1 -1
  183. data/lib/puppet/provider/package/pip.rb +5 -8
  184. data/lib/puppet/provider/package/pip3.rb +1 -1
  185. data/lib/puppet/provider/package/pkg.rb +1 -1
  186. data/lib/puppet/provider/package/pkgdmg.rb +3 -2
  187. data/lib/puppet/provider/package/pkgng.rb +13 -4
  188. data/lib/puppet/provider/package/windows.rb +1 -1
  189. data/lib/puppet/provider/package/yum.rb +1 -1
  190. data/lib/puppet/provider/package/zypper.rb +19 -0
  191. data/lib/puppet/provider/service/debian.rb +2 -2
  192. data/lib/puppet/provider/service/launchd.rb +6 -18
  193. data/lib/puppet/provider/service/systemd.rb +9 -2
  194. data/lib/puppet/provider/sshkey/parsed.rb +1 -1
  195. data/lib/puppet/provider/user/aix.rb +1 -1
  196. data/lib/puppet/provider/user/directoryservice.rb +33 -58
  197. data/lib/puppet/provider/zfs/zfs.rb +1 -1
  198. data/lib/puppet/provider/zpool/zpool.rb +1 -1
  199. data/lib/puppet/reference/configuration.rb +1 -1
  200. data/lib/puppet/reference/providers.rb +1 -1
  201. data/lib/puppet/resource.rb +15 -12
  202. data/lib/puppet/resource/capability_finder.rb +20 -13
  203. data/lib/puppet/resource/catalog.rb +60 -3
  204. data/lib/puppet/resource/status.rb +11 -2
  205. data/lib/puppet/resource/type.rb +28 -38
  206. data/lib/puppet/settings.rb +1 -1
  207. data/lib/puppet/settings/config_file.rb +1 -1
  208. data/lib/puppet/settings/environment_conf.rb +13 -5
  209. data/lib/puppet/ssl/certificate_factory.rb +3 -3
  210. data/lib/puppet/ssl/certificate_request.rb +4 -4
  211. data/lib/puppet/ssl/certificate_signer.rb +1 -1
  212. data/lib/puppet/ssl/validator/default_validator.rb +1 -1
  213. data/lib/puppet/test/test_helper.rb +16 -4
  214. data/lib/puppet/transaction.rb +15 -2
  215. data/lib/puppet/transaction/additional_resource_generator.rb +6 -2
  216. data/lib/puppet/transaction/report.rb +31 -1
  217. data/lib/puppet/transaction/resource_harness.rb +0 -25
  218. data/lib/puppet/type.rb +11 -11
  219. data/lib/puppet/type/augeas.rb +1 -1
  220. data/lib/puppet/type/cron.rb +12 -12
  221. data/lib/puppet/type/file.rb +91 -39
  222. data/lib/puppet/type/file/checksum_value.rb +53 -0
  223. data/lib/puppet/type/file/content.rb +26 -111
  224. data/lib/puppet/type/file/data_sync.rb +84 -0
  225. data/lib/puppet/type/file/ensure.rb +17 -14
  226. data/lib/puppet/type/file/selcontext.rb +1 -1
  227. data/lib/puppet/type/file/source.rb +103 -18
  228. data/lib/puppet/type/filebucket.rb +1 -1
  229. data/lib/puppet/type/interface.rb +8 -3
  230. data/lib/puppet/type/macauthorization.rb +1 -1
  231. data/lib/puppet/type/package.rb +6 -0
  232. data/lib/puppet/type/schedule.rb +1 -1
  233. data/lib/puppet/type/stage.rb +1 -1
  234. data/lib/puppet/type/user.rb +19 -17
  235. data/lib/puppet/type/yumrepo.rb +20 -0
  236. data/lib/puppet/util.rb +109 -22
  237. data/lib/puppet/util/autoload.rb +16 -11
  238. data/lib/puppet/util/checksums.rb +74 -31
  239. data/lib/puppet/util/execution.rb +1 -1
  240. data/lib/puppet/util/http_proxy.rb +72 -0
  241. data/lib/puppet/util/log.rb +2 -0
  242. data/lib/puppet/util/logging.rb +43 -1
  243. data/lib/puppet/util/monkey_patches.rb +2 -2
  244. data/lib/puppet/util/multi_match.rb +51 -0
  245. data/lib/puppet/util/network_device/cisco/device.rb +10 -2
  246. data/lib/puppet/util/network_device/cisco/interface.rb +21 -8
  247. data/lib/puppet/util/network_device/transport/ssh.rb +7 -3
  248. data/lib/puppet/util/network_device/transport/telnet.rb +39 -36
  249. data/lib/puppet/util/plist.rb +130 -0
  250. data/lib/puppet/util/resource_template.rb +1 -1
  251. data/lib/puppet/util/run_mode.rb +2 -2
  252. data/lib/puppet/util/skip_tags.rb +9 -0
  253. data/lib/puppet/util/windows/access_control_entry.rb +1 -1
  254. data/lib/puppet/util/windows/access_control_list.rb +3 -3
  255. data/lib/puppet/util/windows/adsi.rb +4 -4
  256. data/lib/puppet/util/windows/api_types.rb +24 -18
  257. data/lib/puppet/util/windows/com.rb +3 -3
  258. data/lib/puppet/util/windows/error.rb +1 -1
  259. data/lib/puppet/util/windows/file.rb +8 -8
  260. data/lib/puppet/util/windows/principal.rb +23 -14
  261. data/lib/puppet/util/windows/process.rb +78 -11
  262. data/lib/puppet/util/windows/registry.rb +1 -1
  263. data/lib/puppet/util/windows/root_certs.rb +5 -5
  264. data/lib/puppet/util/windows/security.rb +33 -35
  265. data/lib/puppet/util/windows/security_descriptor.rb +1 -1
  266. data/lib/puppet/util/windows/sid.rb +42 -4
  267. data/lib/puppet/util/windows/taskscheduler.rb +15 -15
  268. data/lib/puppet/util/windows/user.rb +10 -10
  269. data/lib/puppet/vendor/deep_merge/deep_merge.gemspec +1 -1
  270. data/lib/puppet/vendor/pathspec/LICENSE +2 -2
  271. data/lib/puppet/vendor/pathspec/README.md +1 -1
  272. data/lib/puppet/vendor/rgen/README.rdoc +1 -1
  273. data/lib/puppet/vendor/semantic/lib/semantic/dependency/module_release.rb +14 -0
  274. data/lib/puppet/version.rb +1 -1
  275. data/lib/semver.rb +17 -1
  276. data/man/man5/puppet.conf.5 +12 -12
  277. data/man/man8/extlookup2hiera.8 +1 -1
  278. data/man/man8/puppet-agent.8 +2 -2
  279. data/man/man8/puppet-apply.8 +2 -2
  280. data/man/man8/puppet-ca.8 +2 -2
  281. data/man/man8/puppet-catalog.8 +2 -2
  282. data/man/man8/puppet-cert.8 +2 -2
  283. data/man/man8/puppet-certificate.8 +2 -2
  284. data/man/man8/puppet-certificate_request.8 +2 -2
  285. data/man/man8/puppet-certificate_revocation_list.8 +2 -2
  286. data/man/man8/puppet-config.8 +3 -3
  287. data/man/man8/puppet-describe.8 +1 -1
  288. data/man/man8/puppet-device.8 +1 -1
  289. data/man/man8/puppet-doc.8 +1 -1
  290. data/man/man8/puppet-epp.8 +2 -2
  291. data/man/man8/puppet-facts.8 +2 -2
  292. data/man/man8/puppet-file.8 +2 -2
  293. data/man/man8/puppet-filebucket.8 +2 -2
  294. data/man/man8/puppet-help.8 +2 -2
  295. data/man/man8/puppet-inspect.8 +2 -2
  296. data/man/man8/puppet-key.8 +2 -2
  297. data/man/man8/puppet-man.8 +2 -2
  298. data/man/man8/puppet-master.8 +2 -2
  299. data/man/man8/puppet-module.8 +9 -9
  300. data/man/man8/puppet-node.8 +2 -2
  301. data/man/man8/puppet-parser.8 +2 -2
  302. data/man/man8/puppet-plugin.8 +2 -2
  303. data/man/man8/puppet-report.8 +2 -2
  304. data/man/man8/puppet-resource.8 +2 -2
  305. data/man/man8/puppet-resource_type.8 +2 -2
  306. data/man/man8/puppet-status.8 +3 -3
  307. data/man/man8/puppet.8 +1 -1
  308. data/spec/fixtures/module.tar.gz +0 -0
  309. data/spec/fixtures/unit/functions/lookup/environments/production/modules/bad_data/lib/puppet/functions/bad_data/data.rb +1 -0
  310. data/spec/fixtures/unit/functions/lookup/environments/production/modules/bad_data/manifests/init.pp +0 -1
  311. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_json/data/empty.json +0 -0
  312. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_json/hiera.yaml +5 -0
  313. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_json/manifests/init.pp +2 -0
  314. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_json/metadata.json +9 -0
  315. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_yaml/data/empty.yaml +1 -0
  316. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_yaml/hiera.yaml +5 -0
  317. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_yaml/manifests/init.pp +2 -0
  318. data/spec/fixtures/unit/functions/lookup/environments/production/modules/empty_yaml/metadata.json +9 -0
  319. data/spec/fixtures/unit/functions/lookup/environments/production/modules/hieraprovider/data/first.json +2 -1
  320. data/spec/fixtures/unit/module/trailing-comma.json +1 -1
  321. data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/manifests/init.pp +3 -1
  322. data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/usee/types/zero.pp +1 -0
  323. data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/types/withuseeone.pp +1 -0
  324. data/spec/fixtures/unit/pops/loaders/loaders/dependent_modules_with_metadata/modules/user/types/withuseezero.pp +1 -0
  325. data/spec/fixtures/unit/provider/package/yum/yum-check-update-broken-notices.txt +187 -0
  326. data/spec/fixtures/unit/provider/sshkey/parsed/sample_with_blank_lines +8 -0
  327. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_fetch_if_not_on_the_local_disk.yml +205 -0
  328. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_not_update_if_content_on_disk_is_up-to-date.yml +213 -0
  329. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_md5/should_update_if_content_differs_on_disk.yml +213 -0
  330. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_mtime_is_older_on_disk.yml +205 -0
  331. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_no_header_specified.yml +197 -0
  332. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_fetch_if_not_on_the_local_disk.yml +205 -0
  333. data/spec/fixtures/vcr/cassettes/Puppet_Type_File/when_sourcing/from_http/using_mtime/should_not_update_if_mtime_is_newer_on_disk.yml +205 -0
  334. data/spec/integration/defaults_spec.rb +14 -2
  335. data/spec/integration/file_system/uniquefile_spec.rb +29 -0
  336. data/spec/integration/module_tool/tar/mini_spec.rb +28 -0
  337. data/spec/integration/node/environment_spec.rb +13 -0
  338. data/spec/integration/parser/dynamic_scoping_spec.rb +67 -0
  339. data/spec/integration/parser/parameter_defaults_spec.rb +336 -0
  340. data/spec/integration/parser/undef_param_spec.rb +8 -0
  341. data/spec/integration/provider/yumrepo_spec.rb +1 -1
  342. data/spec/integration/test/test_helper_spec.rb +28 -0
  343. data/spec/integration/transaction/report_spec.rb +16 -0
  344. data/spec/integration/transaction_spec.rb +11 -0
  345. data/spec/integration/type/file_spec.rb +194 -4
  346. data/spec/integration/type/package_spec.rb +5 -1
  347. data/spec/integration/type/tidy_spec.rb +21 -9
  348. data/spec/integration/util/execution_spec.rb +22 -0
  349. data/spec/integration/util/windows/principal_spec.rb +90 -4
  350. data/spec/integration/util/windows/process_spec.rb +31 -0
  351. data/spec/integration/util/windows/security_spec.rb +6 -6
  352. data/spec/integration/util/windows/user_spec.rb +1 -1
  353. data/spec/integration/util_spec.rb +49 -27
  354. data/spec/lib/puppet_spec/compiler.rb +17 -0
  355. data/spec/lib/puppet_spec/files.rb +2 -2
  356. data/spec/lib/puppet_spec/pops.rb +13 -0
  357. data/spec/shared_behaviours/iterative_functions.rb +1 -1
  358. data/spec/shared_contexts/types_setup.rb +96 -0
  359. data/spec/unit/agent_spec.rb +1 -0
  360. data/spec/unit/application/agent_spec.rb +10 -0
  361. data/spec/unit/application/apply_spec.rb +9 -0
  362. data/spec/unit/configurer/downloader_spec.rb +5 -5
  363. data/spec/unit/configurer_spec.rb +271 -39
  364. data/spec/unit/data_providers/hiera_interpolation_spec.rb +57 -0
  365. data/spec/unit/defaults_spec.rb +15 -0
  366. data/spec/unit/environments_spec.rb +24 -4
  367. data/spec/unit/face/parser_spec.rb +43 -2
  368. data/spec/unit/file_serving/http_metadata_spec.rb +85 -0
  369. data/spec/unit/file_serving/metadata_spec.rb +50 -0
  370. data/spec/unit/file_serving/terminus_selector_spec.rb +12 -2
  371. data/spec/unit/file_system_spec.rb +26 -0
  372. data/spec/unit/functions/assert_type_spec.rb +36 -2
  373. data/spec/unit/functions/defined_spec.rb +2 -2
  374. data/spec/unit/functions/epp_spec.rb +11 -3
  375. data/spec/unit/functions/lookup_spec.rb +58 -13
  376. data/spec/unit/functions/regsubst_spec.rb +1 -1
  377. data/spec/unit/functions/reverse_each_spec.rb +108 -0
  378. data/spec/unit/functions/step_spec.rb +113 -0
  379. data/spec/unit/functions/type_spec.rb +35 -0
  380. data/spec/unit/functions4_spec.rb +61 -5
  381. data/spec/unit/indirector/catalog/compiler_spec.rb +705 -4
  382. data/spec/unit/indirector/file_bucket_file/file_spec.rb +1 -1
  383. data/spec/unit/indirector/indirection_spec.rb +1 -1
  384. data/spec/unit/info_service_spec.rb +94 -32
  385. data/spec/unit/module_spec.rb +14 -0
  386. data/spec/unit/module_tool/applications/builder_spec.rb +4 -4
  387. data/spec/unit/network/authstore_spec.rb +1 -1
  388. data/spec/unit/network/http/connection_spec.rb +1 -0
  389. data/spec/unit/network/http/pool_spec.rb +30 -0
  390. data/spec/unit/node_spec.rb +1 -1
  391. data/spec/unit/parser/compiler_spec.rb +16 -0
  392. data/spec/unit/parser/scope_spec.rb +28 -11
  393. data/spec/unit/pops/evaluator/access_ops_spec.rb +3 -3
  394. data/spec/unit/pops/evaluator/comparison_ops_spec.rb +3 -0
  395. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +7 -1
  396. data/spec/unit/pops/evaluator/evaluator_rspec_helper.rb +4 -4
  397. data/spec/unit/pops/evaluator/json_strict_literal_evaluator_spec.rb +63 -0
  398. data/spec/unit/pops/evaluator/runtime3_converter_spec.rb +6 -0
  399. data/spec/unit/pops/loaders/dependency_loader_spec.rb +53 -0
  400. data/spec/unit/pops/loaders/loaders_spec.rb +44 -1
  401. data/spec/unit/pops/parser/lexer2_spec.rb +112 -3
  402. data/spec/unit/pops/parser/parse_calls_spec.rb +8 -0
  403. data/spec/unit/pops/parser/parser_spec.rb +10 -0
  404. data/spec/unit/pops/parser/source_pos_adapter_spec.rb +26 -0
  405. data/spec/unit/pops/types/iterable_spec.rb +262 -0
  406. data/spec/unit/pops/types/recursion_guard_spec.rb +91 -0
  407. data/spec/unit/pops/types/type_acceptor_spec.rb +105 -0
  408. data/spec/unit/pops/types/type_asserter_spec.rb +43 -0
  409. data/spec/unit/pops/types/type_calculator_spec.rb +275 -373
  410. data/spec/unit/pops/types/type_formatter_spec.rb +280 -0
  411. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +152 -0
  412. data/spec/unit/pops/types/type_parser_spec.rb +58 -13
  413. data/spec/unit/pops/types/types_spec.rb +241 -0
  414. data/spec/unit/pops/validator/validator_spec.rb +100 -43
  415. data/spec/unit/provider/cron/parsed_spec.rb +1 -0
  416. data/spec/unit/provider/macauthorization_spec.rb +5 -2
  417. data/spec/unit/provider/nameservice/directoryservice_spec.rb +14 -19
  418. data/spec/unit/provider/package/appdmg_spec.rb +3 -3
  419. data/spec/unit/provider/package/dnf_spec.rb +16 -0
  420. data/spec/unit/provider/package/pip3_spec.rb +60 -42
  421. data/spec/unit/provider/package/pip_spec.rb +47 -34
  422. data/spec/unit/provider/package/pkgdmg_spec.rb +18 -9
  423. data/spec/unit/provider/package/pkgng_spec.rb +4 -2
  424. data/spec/unit/provider/package/yum_spec.rb +11 -0
  425. data/spec/unit/provider/package/zypper_spec.rb +14 -0
  426. data/spec/unit/provider/service/launchd_spec.rb +17 -35
  427. data/spec/unit/provider/service/systemd_spec.rb +7 -0
  428. data/spec/unit/provider/sshkey/parsed_spec.rb +20 -19
  429. data/spec/unit/provider/user/directoryservice_spec.rb +40 -59
  430. data/spec/unit/resource/capability_finder_spec.rb +28 -15
  431. data/spec/unit/resource/catalog_spec.rb +33 -1
  432. data/spec/unit/resource/type_spec.rb +149 -7
  433. data/spec/unit/resource_spec.rb +96 -57
  434. data/spec/unit/settings/environment_conf_spec.rb +18 -1
  435. data/spec/unit/ssl/certificate_revocation_list_spec.rb +3 -3
  436. data/spec/unit/transaction/report_spec.rb +27 -0
  437. data/spec/unit/transaction/resource_harness_spec.rb +0 -47
  438. data/spec/unit/transaction_spec.rb +5 -0
  439. data/spec/unit/type/file/checksum_spec.rb +6 -0
  440. data/spec/unit/type/file/checksum_value_spec.rb +286 -0
  441. data/spec/unit/type/file/content_spec.rb +12 -193
  442. data/spec/unit/type/file/source_spec.rb +211 -119
  443. data/spec/unit/type/file_spec.rb +133 -34
  444. data/spec/unit/type/interface_spec.rb +32 -0
  445. data/spec/unit/type/macauthorization_spec.rb +4 -1
  446. data/spec/unit/type/yumrepo_spec.rb +2 -2
  447. data/spec/unit/util/filetype_spec.rb +1 -1
  448. data/spec/unit/util/http_proxy_spec.rb +2 -2
  449. data/spec/unit/util/log/destinations_spec.rb +0 -2
  450. data/spec/unit/util/logging_spec.rb +69 -0
  451. data/spec/unit/util/multi_match_spec.rb +39 -0
  452. data/spec/unit/util/network_device/cisco/device_spec.rb +253 -216
  453. data/spec/unit/util/network_device/transport/telnet_spec.rb +60 -58
  454. data/spec/unit/util/plist_spec.rb +110 -0
  455. data/spec/unit/util/resource_template_spec.rb +2 -2
  456. data/spec/unit/util/run_mode_spec.rb +27 -3
  457. data/spec/unit/util/windows/adsi_spec.rb +4 -4
  458. data/spec/unit/util/windows/api_types_spec.rb +42 -0
  459. data/spec/unit/util/windows/security_descriptor_spec.rb +3 -3
  460. data/spec/unit/util/windows/sid_spec.rb +1 -1
  461. data/spec/unit/util_spec.rb +123 -13
  462. data/tasks/cfpropertylist.rake +15 -0
  463. metadata +114 -26
  464. data/lib/puppet/vendor/load_plist.rb +0 -1
  465. data/lib/puppet/vendor/plist/CHANGELOG +0 -82
  466. data/lib/puppet/vendor/plist/MIT-LICENSE +0 -21
  467. data/lib/puppet/vendor/plist/PUPPET_README.md +0 -6
  468. data/lib/puppet/vendor/plist/README +0 -36
  469. data/lib/puppet/vendor/plist/Rakefile +0 -144
  470. data/lib/puppet/vendor/plist/docs/USAGE +0 -104
  471. data/lib/puppet/vendor/plist/docs/jamis-template.rb +0 -591
  472. data/lib/puppet/vendor/plist/lib/plist.rb +0 -22
  473. data/lib/puppet/vendor/plist/lib/plist/generator.rb +0 -224
  474. data/lib/puppet/vendor/plist/lib/plist/parser.rb +0 -225
  475. data/lib/puppet/vendor/plist/test/assets/AlbumData.xml +0 -203
  476. data/lib/puppet/vendor/plist/test/assets/Cookies.plist +0 -104
  477. data/lib/puppet/vendor/plist/test/assets/commented.plist +0 -9
  478. data/lib/puppet/vendor/plist/test/assets/example_data.bin +0 -0
  479. data/lib/puppet/vendor/plist/test/assets/example_data.jpg +0 -0
  480. data/lib/puppet/vendor/plist/test/assets/example_data.plist +0 -259
  481. data/lib/puppet/vendor/plist/test/assets/test_data_elements.plist +0 -24
  482. data/lib/puppet/vendor/plist/test/assets/test_empty_key.plist +0 -13
  483. data/lib/puppet/vendor/plist/test/test_data_elements.rb +0 -115
  484. data/lib/puppet/vendor/plist/test/test_generator.rb +0 -59
  485. data/lib/puppet/vendor/plist/test/test_generator_basic_types.rb +0 -58
  486. data/lib/puppet/vendor/plist/test/test_generator_collections.rb +0 -82
  487. data/lib/puppet/vendor/plist/test/test_parser.rb +0 -90
@@ -1,11 +1,26 @@
1
+ require_relative 'iterable'
2
+ require_relative 'enumeration'
3
+ require_relative 'recursion_guard'
4
+ require_relative 'type_acceptor'
5
+ require_relative 'type_asserter'
6
+ require_relative 'type_assertion_error'
7
+ require_relative 'type_formatter'
8
+ require_relative 'type_calculator'
9
+ require_relative 'type_factory'
10
+ require_relative 'type_parser'
11
+ require_relative 'class_loader'
12
+ require_relative 'type_mismatch_describer'
13
+
1
14
  require 'rgen/metamodel_builder'
2
15
 
16
+ module Puppet::Pops
17
+ module Types
3
18
  # The Types model is a model of Puppet Language types.
4
19
  #
5
20
  # The exact relationship between types is not visible in this model wrt. the PDataType which is an abstraction
6
21
  # of Scalar, Array[Data], and Hash[Scalar, Data] nested to any depth. This means it is not possible to
7
- # infer the type by simply looking at the inheritance hierarchy. The {Puppet::Pops::Types::TypeCalculator} should
8
- # be used to answer questions about types. The {Puppet::Pops::Types::TypeFactory} should be used to create an instance
22
+ # infer the type by simply looking at the inheritance hierarchy. The {TypeCalculator} should
23
+ # be used to answer questions about types. The {TypeFactory} should be used to create an instance
9
24
  # of a type whenever one is needed.
10
25
  #
11
26
  # The implementation of the Types model contains methods that are required for the type objects to behave as
@@ -14,1535 +29,2330 @@ require 'rgen/metamodel_builder'
14
29
  #
15
30
  # @api public
16
31
  #
17
- module Puppet::Pops
18
- # TODO: See PUP-2978 for possible performance optimization
19
- module Types
20
- class TypedModelObject < Object
21
- include Puppet::Pops::Visitable
22
- include Puppet::Pops::Adaptable
23
- end
24
-
25
- # Base type for all types
26
- # @api public
27
- #
28
- class PAnyType < TypedModelObject
29
- # Checks if _o_ is a type that is assignable to this type.
30
- # If _o_ is a `Class` then it is first converted to a type.
31
- # If _o_ is a Variant, then it is considered assignable when all its types are assignable
32
- # @return [Boolean] `true` when _o_ is assignable to this type
33
- # @api public
34
- def assignable?(o)
35
- case o
36
- when Class
37
- # Safe to call _assignable directly since a Class never is a Unit or Variant
38
- _assignable?(Puppet::Pops::Types::TypeCalculator.singleton.type(o))
39
- when PUnitType
32
+ # TODO: See PUP-2978 for possible performance optimization
33
+ class TypedModelObject < Object
34
+ include Visitable
35
+ include Adaptable
36
+ end
37
+
38
+ # Base type for all types
39
+ # @api public
40
+ #
41
+ class PAnyType < TypedModelObject
42
+ # Accept a visitor that will be sent the message `visit`, once with `self` as the
43
+ # argument. The visitor will then visit all types that this type contains.
44
+ #
45
+ def accept(visitor, guard)
46
+ visitor.visit(self, guard)
47
+ end
48
+
49
+ # Checks if _o_ is a type that is assignable to this type.
50
+ # If _o_ is a `Class` then it is first converted to a type.
51
+ # If _o_ is a Variant, then it is considered assignable when all its types are assignable
52
+ #
53
+ # The check for assignable must be guarded against self recursion since `self`, the given type _o_,
54
+ # or both, might be a `TypeAlias`. The initial caller of this method will typically never care
55
+ # about this and hence pass only the first argument, but as soon as a check of a contained type
56
+ # encounters a `TypeAlias`, then a `RecursionGuard` instance is created and passed on in all
57
+ # subsequent calls. The recursion is allowed to continue until self recursion has been detected in
58
+ # both `self` and in the given type. At that point the given type is considered to be assignable
59
+ # to `self` since all checks up to that point were positive.
60
+ #
61
+ # @param o [Class,PAnyType] the class or type to test
62
+ # @param guard [RecursionGuard] guard against recursion. Only used by internal calls
63
+ # @return [Boolean] `true` when _o_ is assignable to this type
64
+ # @api public
65
+ def assignable?(o, guard = nil)
66
+ case o
67
+ when Class
68
+ # Safe to call _assignable directly since a Class never is a Unit or Variant
69
+ _assignable?(TypeCalculator.singleton.type(o), guard)
70
+ when PUnitType
71
+ true
72
+ when PTypeAliasType
73
+ # An alias may contain self recursive constructs.
74
+ if o.self_recursion?
75
+ guard ||= RecursionGuard.new
76
+ if guard.add_that(o) == RecursionGuard::SELF_RECURSION_IN_BOTH
77
+ # Recursion detected both in self and other. This means that other is assignable
78
+ # to self. This point would not have been reached otherwise
40
79
  true
41
- when PVariantType
42
- # Assignable if all contained types are assignable
43
- o.types.all? { |vt| assignable?(vt) }
44
- when PNotUndefType
45
- if !(o.type.nil? || o.type.assignable?(PUndefType::DEFAULT))
46
- assignable?(o.type)
47
- else
48
- _assignable?(o)
49
- end
50
80
  else
51
- _assignable?(o)
81
+ assignable?(o.resolved_type, guard)
52
82
  end
53
- end
83
+ else
84
+ assignable?(o.resolved_type, guard)
85
+ end
86
+ when PVariantType
87
+ # Assignable if all contained types are assignable
88
+ o.types.all? { |vt| assignable?(vt, guard) }
89
+ when PNotUndefType
90
+ if !(o.type.nil? || o.type.assignable?(PUndefType::DEFAULT))
91
+ assignable?(o.type, guard)
92
+ else
93
+ _assignable?(o, guard)
94
+ end
95
+ else
96
+ _assignable?(o, guard)
97
+ end
98
+ end
54
99
 
55
- # Returns `true` if this instance is a callable that accepts the given _args_
56
- #
57
- # @return [Boolean]
58
- def callable?(args)
59
- args.is_a?(PAnyType) && kind_of_callable? && args.callable_args?(self)
60
- end
100
+ # Returns `true` if this instance is a callable that accepts the given _args_
101
+ #
102
+ # @param args [PAnyType] the arguments to test
103
+ # @param guard [RecursionGuard] guard against recursion. Only used by internal calls
104
+ # @return [Boolean] `true` if this instance is a callable that accepts the given _args_
105
+ def callable?(args, guard = nil)
106
+ args.is_a?(PAnyType) && kind_of_callable? && args.callable_args?(self, guard)
107
+ end
61
108
 
62
- # Returns `true` if this instance is considered valid as arguments to _callable_
63
- # @return [Boolean]
64
- # @api private
65
- def callable_args?(callable)
66
- false
67
- end
109
+ # Returns `true` if this instance is considered valid as arguments to the given `callable`
110
+ # @param callable [PAnyType] the callable
111
+ # @param guard [RecursionGuard] guard against recursion. Only used by internal calls
112
+ # @return [Boolean] `true` if this instance is considered valid as arguments to the given `callable`
113
+ # @api private
114
+ def callable_args?(callable, guard)
115
+ false
116
+ end
68
117
 
69
- # Subclasses that are enumerable will override this method to return `true`
70
- # @return [Boolean] false#
71
- def enumerable?
72
- is_a?(Enumerable)
73
- end
118
+ # Generalizes value specific types. Types that are not value specific will return `self` otherwise
119
+ # the generalized type is returned.
120
+ #
121
+ # @return [PAnyType] The generalized type
122
+ # @api public
123
+ def generalize
124
+ # Applicable to all types that have no variables
125
+ self
126
+ end
74
127
 
75
- # Generalizes value specific types. Types that are not value specific will return `self` otherwize
76
- # the generalized type is returned.
77
- #
78
- # @return [PAnyType] The generalized type
79
- # @api public
80
- def generalize
81
- # Applicable to all types that have no variables
82
- self
83
- end
128
+ # Normalizes the type. This does not change the characteristics of the type but it will remove duplicates
129
+ # and constructs like NotUndef[T] where T is not assignable from Undef and change Variant[*T] where all
130
+ # T are enums into an Enum.
131
+ #
132
+ # @param guard [RecursionGuard] guard against recursion. Only used by internal calls
133
+ # @return [PAnyType] The iterable type that this type is assignable to or `nil`
134
+ # @api public
135
+ def normalize(guard = nil)
136
+ self
137
+ end
84
138
 
85
- # Responds `true` for all callables, variants of callables and unless _optional_ is
86
- # false, all optional callables.
87
- # @return [Boolean] `true`if this type is considered callable
88
- # @api private
89
- def kind_of_callable?(optional=true)
90
- false
91
- end
139
+ # Responds `true` for all callables, variants of callables and unless _optional_ is
140
+ # false, all optional callables.
141
+ # @param optional [Boolean]
142
+ # @param guard [RecursionGuard] guard against recursion. Only used by internal calls
143
+ # @return [Boolean] `true`if this type is considered callable
144
+ # @api private
145
+ def kind_of_callable?(optional = true, guard = nil)
146
+ false
147
+ end
92
148
 
93
- def hash
94
- self.class.hash
95
- end
149
+ # Returns `true` if an instance of this type is iterable, `false` otherwise
150
+ # The method #iterable_type must produce a `PIterableType` instance when this
151
+ # method returns `true`
152
+ #
153
+ # @param guard [RecursionGuard] guard against recursion. Only used by internal calls
154
+ # @return [Boolean] flag to indicate if instances of this type is iterable.
155
+ def iterable?(guard = nil)
156
+ false
157
+ end
96
158
 
97
- # Returns true if the given argument _o_ is an instance of this type
98
- # @return [Boolean]
99
- def instance?(o)
100
- true
101
- end
159
+ # Returns the `PIterableType` that this type should be assignable to, or `nil` if no such type exists.
160
+ # A type that returns a `PIterableType` must respond `true` to `#iterable?`.
161
+ #
162
+ # @example
163
+ # Any Collection[T] is assignable to an Iterable[T]
164
+ # A String is assignable to an Iterable[String] iterating over the strings characters
165
+ # An Integer is assignable to an Iterable[Integer] iterating over the 'times' enumerator
166
+ # A Type[T] is assignable to an Iterable[Type[T]] if T is an Integer or Enum
167
+ #
168
+ # @param guard [RecursionGuard] guard against recursion. Only used by internal calls
169
+ # @return [PIterableType,nil] The iterable type that this type is assignable to or `nil`
170
+ # @api private
171
+ def iterable_type(guard = nil)
172
+ nil
173
+ end
102
174
 
103
- def ==(o)
104
- self.class == o.class
105
- end
175
+ def hash
176
+ self.class.hash
177
+ end
106
178
 
107
- alias eql? ==
179
+ # Returns true if the given argument _o_ is an instance of this type
180
+ # @return [Boolean]
181
+ def instance?(o)
182
+ true
183
+ end
108
184
 
109
- # Strips the class name from all module prefixes, the leading 'P' and the ending 'Type'. I.e.
110
- # an instance of Puppet::Pops::Types::PVariantType will return 'Variant'
111
- # @return [String] the simple name of this type
112
- def simple_name
113
- n = self.class.name
114
- n[n.rindex('::')+3..n.size-5]
115
- end
185
+ def eql?(o)
186
+ self.class == o.class
187
+ end
116
188
 
117
- def to_s
118
- Puppet::Pops::Types::TypeCalculator.string(self)
119
- end
189
+ def ==(o)
190
+ eql?(o)
191
+ end
120
192
 
121
- # The default instance of this type. Each type in the type system has this constant
122
- # declared.
123
- #
124
- DEFAULT = PAnyType.new
193
+ # Strips the class name from all module prefixes, the leading 'P' and the ending 'Type'. I.e.
194
+ # an instance of PVariantType will return 'Variant'
195
+ # @return [String] the simple name of this type
196
+ def simple_name
197
+ n = self.class.name
198
+ n[n.rindex('::')+3..n.size-5]
199
+ end
125
200
 
126
- protected
201
+ def to_alias_expanded_s
202
+ TypeFormatter.new.alias_expanded_string(self)
203
+ end
127
204
 
128
- # @api private
129
- def _assignable?(o)
130
- o.is_a?(PAnyType)
131
- end
205
+ def to_s
206
+ TypeFormatter.string(self)
207
+ end
132
208
 
133
- NAME_SEGMENT_SEPARATOR = '::'.freeze
209
+ # The default instance of this type. Each type in the type system has this constant
210
+ # declared.
211
+ #
212
+ DEFAULT = PAnyType.new
134
213
 
135
- # @api private
136
- def class_from_string(str)
137
- begin
138
- str.split(NAME_SEGMENT_SEPARATOR).reduce(Object) do |memo, name_segment|
139
- memo.const_get(name_segment)
140
- end
141
- rescue NameError
142
- return nil
143
- end
144
- end
214
+ protected
145
215
 
146
- # Produces the tuple entry at the given index given a tuple type, its from/to constraints on the last
147
- # type, and an index.
148
- # Produces nil if the index is out of bounds
149
- # from must be less than to, and from may not be less than 0
150
- #
151
- # @api private
152
- #
153
- def tuple_entry_at(tuple_t, from, to, index)
154
- regular = (tuple_t.types.size - 1)
155
- if index < regular
156
- tuple_t.types[index]
157
- elsif index < regular + to
158
- # in the varargs part
159
- tuple_t.types[-1]
160
- else
161
- nil
162
- end
163
- end
216
+ # @api private
217
+ def _assignable?(o, guard)
218
+ o.is_a?(PAnyType)
219
+ end
164
220
 
221
+ NAME_SEGMENT_SEPARATOR = '::'.freeze
165
222
 
166
- # Transform size_type to min, max
167
- # if size_type == nil the constraint is 1,1
168
- # if size_type.from == nil min size = 1
169
- # if size_type.to == nil max size == Infinity
170
- #
171
- # @api private
172
- def type_to_range(size_type)
173
- return [1,1] if size_type.nil?
174
- from = size_type.from
175
- to = size_type.to
176
- [from.nil? ? 1 : from, to.nil? ? Float::INFINITY : to]
223
+ # @api private
224
+ def class_from_string(str)
225
+ begin
226
+ str.split(NAME_SEGMENT_SEPARATOR).reduce(Object) do |memo, name_segment|
227
+ memo.const_get(name_segment)
177
228
  end
229
+ rescue NameError
230
+ return nil
178
231
  end
232
+ end
179
233
 
180
- # The type of types.
181
- # @api public
182
- #
183
- class PType < PAnyType
184
- attr_reader :type
185
-
186
- def initialize(type)
187
- @type = type
188
- end
234
+ # Produces the tuple entry at the given index given a tuple type, its from/to constraints on the last
235
+ # type, and an index.
236
+ # Produces nil if the index is out of bounds
237
+ # from must be less than to, and from may not be less than 0
238
+ #
239
+ # @api private
240
+ #
241
+ def tuple_entry_at(tuple_t, from, to, index)
242
+ regular = (tuple_t.types.size - 1)
243
+ if index < regular
244
+ tuple_t.types[index]
245
+ elsif index < regular + to
246
+ # in the varargs part
247
+ tuple_t.types[-1]
248
+ else
249
+ nil
250
+ end
251
+ end
189
252
 
190
- def instance?(o)
191
- if o.is_a?(PAnyType)
192
- type.nil? || type.assignable?(o)
193
- else
194
- assignable?(TypeCalculator.infer(o))
195
- end
196
- end
197
253
 
198
- def generalize
199
- @type.nil? ? DEFAULT : PType.new(type.generalize)
200
- end
254
+ # Transform size_type to min, max
255
+ # if size_type == nil the constraint is 1,1
256
+ # if size_type.from == nil min size = 1
257
+ # if size_type.to == nil max size == Infinity
258
+ #
259
+ # @api private
260
+ def type_to_range(size_type)
261
+ return [1,1] if size_type.nil?
262
+ from = size_type.from
263
+ to = size_type.to
264
+ [from.nil? ? 1 : from, to.nil? ? Float::INFINITY : to]
265
+ end
201
266
 
202
- def hash
203
- 31 * @type.hash
204
- end
267
+ # Applies a transformation by sending the given _method_ and _method_args_ to each of the types of the given array
268
+ # and collecting the results in a new array. If all transformation calls returned the type instance itself (i.e. no
269
+ # transformation took place), then this method will return `self`. If a transformation did occur, then this method
270
+ # will either return the transformed array or in case a block was given, the result of calling a given block with
271
+ # the transformed array.
272
+ #
273
+ # @param types [Array<PAnyType>] the array of types to transform
274
+ # @param method [Symbol] The method to call on each type
275
+ # @param method_args [Object] The arguments to pass to the method, if any
276
+ # @return [Object] self, the transformed array, or the result of calling a given block with the transformed array
277
+ # @yieldparam altered_types [Array<PAnyType>] the altered type array
278
+ # @api private
279
+ def alter_type_array(types, method, *method_args)
280
+ modified = false
281
+ modified_types = types.map do |t|
282
+ t_mod = t.send(method, *method_args)
283
+ modified = !t.equal?(t_mod) unless modified
284
+ t_mod
285
+ end
286
+ if modified
287
+ block_given? ? yield(modified_types) : modified_types
288
+ else
289
+ self
290
+ end
291
+ end
292
+ end
205
293
 
206
- def ==(o)
207
- self.class == o.class && @type == o.type
208
- end
294
+ # @abstract Encapsulates common behavior for a type that contains one type
295
+ # @api public
296
+ class PTypeWithContainedType < PAnyType
297
+ attr_reader :type
209
298
 
210
- def simple_name
211
- # since this the class is inconsistently named PType and not PTypeType
212
- 'Type'
213
- end
299
+ def initialize(type)
300
+ @type = type
301
+ end
214
302
 
215
- DEFAULT = PType.new(nil)
303
+ def accept(visitor, guard)
304
+ super
305
+ @type.accept(visitor, guard) unless @type.nil?
306
+ end
216
307
 
217
- protected
308
+ def generalize
309
+ if @type.nil?
310
+ self.class::DEFAULT
311
+ else
312
+ ge_type = @type.generalize
313
+ @type.equal?(ge_type) ? self : self.class.new(ge_type)
314
+ end
315
+ end
218
316
 
219
- # @api private
220
- def _assignable?(o)
221
- return false unless o.is_a?(PType)
222
- return true if @type.nil? # wide enough to handle all types
223
- return false if o.type.nil? # wider than t
224
- @type.assignable?(o.type)
225
- end
317
+ def normalize(guard = nil)
318
+ if @type.nil?
319
+ self.class::DEFAULT
320
+ else
321
+ ne_type = @type.normalize(guard)
322
+ @type.equal?(ne_type) ? self : self.class.new(ne_type)
226
323
  end
324
+ end
227
325
 
228
- class PNotUndefType < PAnyType
229
- attr_reader :type
326
+ def hash
327
+ self.class.hash * 31 * @type.hash
328
+ end
230
329
 
231
- def initialize(type = nil)
232
- @type = type.class == PAnyType ? nil : type
233
- end
330
+ def eql?(o)
331
+ self.class == o.class && @type == o.type
332
+ end
333
+ end
234
334
 
235
- def instance?(o)
236
- !(o.nil? || o == :undef) && (@type.nil? || @type.instance?(o))
237
- end
335
+ # The type of types.
336
+ # @api public
337
+ #
338
+ class PType < PTypeWithContainedType
339
+ def instance?(o)
340
+ if o.is_a?(PAnyType)
341
+ type.nil? || type.assignable?(o)
342
+ else
343
+ assignable?(TypeCalculator.infer(o))
344
+ end
345
+ end
238
346
 
239
- def generalize
240
- @type.nil? ? DEFAULT : PNotUndefType.new(type.generalize)
241
- end
347
+ def iterable?(guard = nil)
348
+ case @type
349
+ when PEnumType
350
+ true
351
+ when PIntegerType
352
+ @type.finite_range?
353
+ else
354
+ false
355
+ end
356
+ end
242
357
 
243
- def hash
244
- 31 * @type.hash
245
- end
358
+ def iterable_type(guard = nil)
359
+ # The types PIntegerType and PEnumType are Iterable
360
+ case @type
361
+ when PEnumType
362
+ # @type describes the element type perfectly since the iteration is made over the
363
+ # contained choices.
364
+ PIterableType.new(@type)
365
+ when PIntegerType
366
+ # @type describes the element type perfectly since the iteration is made over the
367
+ # specified range.
368
+ @type.finite_range? ? PIterableType.new(@type) : nil
369
+ else
370
+ nil
371
+ end
372
+ end
246
373
 
247
- def ==(o)
248
- self.class == o.class && @type == o.type
249
- end
374
+ def eql?(o)
375
+ self.class == o.class && @type == o.type
376
+ end
250
377
 
251
- DEFAULT = PNotUndefType.new
378
+ def simple_name
379
+ # since this the class is inconsistently named PType and not PTypeType
380
+ 'Type'
381
+ end
252
382
 
253
- protected
383
+ DEFAULT = PType.new(nil)
254
384
 
255
- # @api private
256
- def _assignable?(o)
257
- o.is_a?(PAnyType) && !o.assignable?(PUndefType::DEFAULT) && (@type.nil? || @type.assignable?(o))
258
- end
259
- end
385
+ protected
260
386
 
261
- # @api public
262
- #
263
- class PUndefType < PAnyType
264
- def instance?(o)
265
- o.nil? || o == :undef
266
- end
387
+ # @api private
388
+ def _assignable?(o, guard)
389
+ return false unless o.is_a?(PType)
390
+ return true if @type.nil? # wide enough to handle all types
391
+ return false if o.type.nil? # wider than t
392
+ @type.assignable?(o.type, guard)
393
+ end
394
+ end
267
395
 
268
- # @api private
269
- def callable_args?(callable_t)
270
- # if callable_t is Optional (or indeed PUndefType), this means that 'missing callable' is accepted
271
- callable_t.assignable?(DEFAULT)
272
- end
396
+ class PNotUndefType < PTypeWithContainedType
397
+ def initialize(type = nil)
398
+ super(type.class == PAnyType ? nil : type)
399
+ end
273
400
 
274
- DEFAULT = PUndefType.new
401
+ def instance?(o)
402
+ !(o.nil? || o == :undef) && (@type.nil? || @type.instance?(o))
403
+ end
275
404
 
276
- protected
277
- # @api private
278
- def _assignable?(o)
279
- o.is_a?(PUndefType)
280
- end
405
+ def normalize(guard = nil)
406
+ n = super
407
+ if n.type.nil?
408
+ n
409
+ else
410
+ if n.type.is_a?(POptionalType)
411
+ # No point in having an optional in a NotUndef
412
+ PNotUndef.new(n.type.type).normalize
413
+ elsif !n.type.assignable?(PUndefType::DEFAULT)
414
+ # THe type is NotUndef anyway, so it can be stripped of
415
+ n.type
416
+ else
417
+ n
418
+ end
281
419
  end
420
+ end
282
421
 
283
- # A type private to the type system that describes "ignored type" - i.e. "I am what you are"
284
- # @api private
285
- #
286
- class PUnitType < PAnyType
287
- def instance?(o)
288
- true
289
- end
422
+ DEFAULT = PNotUndefType.new
290
423
 
291
- DEFAULT = PUnitType.new
424
+ protected
292
425
 
293
- protected
294
- # @api private
295
- def _assignable?(o)
296
- true
297
- end
298
- end
426
+ # @api private
427
+ def _assignable?(o, guard)
428
+ o.is_a?(PAnyType) && !o.assignable?(PUndefType::DEFAULT, guard) && (@type.nil? || @type.assignable?(o, guard))
429
+ end
430
+ end
299
431
 
300
- # @api public
301
- #
302
- class PDefaultType < PAnyType
303
- def instance?(o)
304
- o == :default
305
- end
432
+ # @api public
433
+ #
434
+ class PUndefType < PAnyType
435
+ def instance?(o)
436
+ o.nil? || o == :undef
437
+ end
306
438
 
307
- DEFAULT = PDefaultType.new
439
+ # @api private
440
+ def callable_args?(callable_t, guard)
441
+ # if callable_t is Optional (or indeed PUndefType), this means that 'missing callable' is accepted
442
+ callable_t.assignable?(DEFAULT, guard)
443
+ end
308
444
 
309
- protected
310
- # @api private
311
- def _assignable?(o)
312
- o.is_a?(PDefaultType)
313
- end
314
- end
445
+ DEFAULT = PUndefType.new
315
446
 
316
- # A flexible data type, being assignable to its subtypes as well as PArrayType and PHashType with element type assignable to PDataType.
317
- #
318
- # @api public
319
- #
320
- class PDataType < PAnyType
321
- def ==(o)
322
- self.class == o.class || o.class == PVariantType && o == PVariantType::DATA
323
- end
447
+ protected
448
+ # @api private
449
+ def _assignable?(o, guard)
450
+ o.is_a?(PUndefType)
451
+ end
452
+ end
324
453
 
325
- def instance?(o)
326
- PVariantType::DATA.instance?(o)
327
- end
454
+ # A type private to the type system that describes "ignored type" - i.e. "I am what you are"
455
+ # @api private
456
+ #
457
+ class PUnitType < PAnyType
458
+ def instance?(o)
459
+ true
460
+ end
328
461
 
329
- DEFAULT = PDataType.new
462
+ DEFAULT = PUnitType.new
330
463
 
331
- protected
464
+ protected
465
+ # @api private
466
+ def _assignable?(o, guard)
467
+ true
468
+ end
469
+ end
332
470
 
333
- # Data is assignable by other Data and by Array[Data] and Hash[Scalar, Data]
334
- # @api private
335
- def _assignable?(o)
336
- # We cannot put the NotUndefType[Data] in the @data_variant_t since that causes an endless recursion
337
- case o
338
- when Types::PDataType
339
- true
340
- when Types::PNotUndefType
341
- assignable?(o.type || PUndefType::DEFAULT)
342
- else
343
- PVariantType::DATA.assignable?(o)
344
- end
345
- end
346
- end
471
+ # @api public
472
+ #
473
+ class PDefaultType < PAnyType
474
+ def instance?(o)
475
+ o == :default
476
+ end
347
477
 
348
- # Type that is PDataType compatible, but is not a PCollectionType.
349
- # @api public
350
- #
351
- class PScalarType < PAnyType
478
+ DEFAULT = PDefaultType.new
352
479
 
353
- def instance?(o)
354
- assignable?(TypeCalculator.infer(o))
355
- end
480
+ protected
481
+ # @api private
482
+ def _assignable?(o, guard)
483
+ o.is_a?(PDefaultType)
484
+ end
485
+ end
356
486
 
357
- DEFAULT = PScalarType.new
487
+ # A flexible data type, being assignable to its subtypes as well as PArrayType and PHashType with element type assignable to PDataType.
488
+ #
489
+ # @api public
490
+ #
491
+ class PDataType < PAnyType
492
+ def eql?(o)
493
+ self.class == o.class || o == PVariantType::DATA
494
+ end
358
495
 
359
- protected
496
+ def instance?(o)
497
+ PVariantType::DATA.instance?(o)
498
+ end
360
499
 
361
- # @api private
362
- def _assignable?(o)
363
- o.is_a?(PScalarType)
364
- end
500
+ DEFAULT = PDataType.new
501
+
502
+ protected
503
+
504
+ # Data is assignable by other Data and by Array[Data] and Hash[Scalar, Data]
505
+ # @api private
506
+ def _assignable?(o, guard)
507
+ # We cannot put the NotUndefType[Data] in the @data_variant_t since that causes an endless recursion
508
+ case o
509
+ when Types::PDataType
510
+ true
511
+ when Types::PNotUndefType
512
+ assignable?(o.type || PUndefType::DEFAULT, guard)
513
+ else
514
+ PVariantType::DATA.assignable?(o, guard)
365
515
  end
516
+ end
517
+ end
366
518
 
367
- # A string type describing the set of strings having one of the given values
368
- # @api public
369
- #
370
- class PEnumType < PScalarType
371
- include Enumerable
519
+ # Type that is PDataType compatible, but is not a PCollectionType.
520
+ # @api public
521
+ #
522
+ class PScalarType < PAnyType
372
523
 
373
- attr_reader :values
524
+ def instance?(o)
525
+ assignable?(TypeCalculator.infer(o))
526
+ end
374
527
 
375
- def initialize(values)
376
- @values = values.sort.freeze
377
- end
528
+ DEFAULT = PScalarType.new
378
529
 
379
- # Returns Enumerator if no block is given, otherwise, calls the given
380
- # block with each of the strings for this enum
381
- def each
382
- if block_given?
383
- values.each { |x| yield x }
384
- else
385
- values.to_enum
386
- end
387
- end
530
+ protected
388
531
 
389
- def hash
390
- @values.hash
391
- end
532
+ # @api private
533
+ def _assignable?(o, guard)
534
+ o.is_a?(PScalarType)
535
+ end
536
+ end
392
537
 
393
- def ==(o)
394
- self.class == o.class && @values == o.values
395
- end
538
+ # A string type describing the set of strings having one of the given values
539
+ # @api public
540
+ #
541
+ class PEnumType < PScalarType
542
+ attr_reader :values
396
543
 
397
- DEFAULT = PEnumType.new([])
544
+ def initialize(values)
545
+ @values = values.sort.freeze
546
+ end
398
547
 
399
- protected
548
+ # Returns Enumerator if no block is given, otherwise, calls the given
549
+ # block with each of the strings for this enum
550
+ def each(&block)
551
+ r = Iterable.on(self)
552
+ block_given? ? r.each(&block) : r
553
+ end
400
554
 
401
- # @api private
402
- def _assignable?(o)
403
- return true if self == o
404
- svalues = values
405
- if svalues.empty?
406
- return true if o.is_a?(PStringType) || o.is_a?(PEnumType) || o.is_a?(PPatternType)
407
- end
408
- case o
409
- when PStringType
410
- # if the set of strings are all found in the set of enums
411
- !o.values.empty? && o.values.all? { |s| svalues.any? { |e| e == s }}
412
- when PEnumType
413
- !o.values.empty? && o.values.all? { |s| svalues.any? {|e| e == s }}
414
- else
415
- false
416
- end
417
- end
418
- end
555
+ def iterable?(guard = nil)
556
+ true
557
+ end
419
558
 
420
- # @api public
421
- #
422
- class PNumericType < PScalarType
423
- def initialize(from, to = Float::INFINITY)
424
- from = -Float::INFINITY if from.nil? || from == :default
425
- to = Float::INFINITY if to.nil? || to == :default
426
- raise ArgumentError, "'from' must be less or equal to 'to'. Got (#{from}, #{to}" if from.is_a?(Numeric) && to.is_a?(Numeric) && from > to
427
- @from = from
428
- @to = to
429
- end
559
+ def iterable_type(guard = nil)
560
+ # An instance of an Enum is a String
561
+ PStringType::ITERABLE_TYPE
562
+ end
430
563
 
431
- # Returns the lower bound of the numeric range or `nil` if no lower bound is set.
432
- # @return [Float,Integer]
433
- def from
434
- @from == -Float::INFINITY ? nil : @from
435
- end
564
+ def hash
565
+ @values.hash
566
+ end
436
567
 
437
- # Returns the upper bound of the numeric range or `nil` if no upper bound is set.
438
- # @return [Float,Integer]
439
- def to
440
- @to == Float::INFINITY ? nil : @to
441
- end
568
+ def eql?(o)
569
+ self.class == o.class && @values == o.values
570
+ end
442
571
 
443
- # Same as #from but will return `-Float::Infinity` instead of `nil` if no lower bound is set.
444
- # @return [Float,Integer]
445
- def numeric_from
446
- @from
447
- end
572
+ DEFAULT = PEnumType.new(EMPTY_ARRAY)
448
573
 
449
- # Same as #to but will return `Float::Infinity` instead of `nil` if no lower bound is set.
450
- # @return [Float,Integer]
451
- def numeric_to
452
- @to
453
- end
574
+ protected
454
575
 
455
- def hash
456
- @from.hash * 31 + @to.hash
457
- end
576
+ # @api private
577
+ def _assignable?(o, guard)
578
+ return true if self == o
579
+ svalues = values
580
+ if svalues.empty?
581
+ return true if o.is_a?(PStringType) || o.is_a?(PEnumType) || o.is_a?(PPatternType)
582
+ end
583
+ case o
584
+ when PStringType
585
+ # if the set of strings are all found in the set of enums
586
+ !o.values.empty? && o.values.all? { |s| svalues.any? { |e| e == s }}
587
+ when PEnumType
588
+ !o.values.empty? && o.values.all? { |s| svalues.any? {|e| e == s }}
589
+ else
590
+ false
591
+ end
592
+ end
593
+ end
458
594
 
459
- def ==(o)
460
- self.class == o.class && @from == o.numeric_from && @to == o.numeric_to
461
- end
595
+ # @api public
596
+ #
597
+ class PNumericType < PScalarType
598
+ def initialize(from, to = Float::INFINITY)
599
+ from = -Float::INFINITY if from.nil? || from == :default
600
+ to = Float::INFINITY if to.nil? || to == :default
601
+ raise ArgumentError, "'from' must be less or equal to 'to'. Got (#{from}, #{to}" if from.is_a?(Numeric) && to.is_a?(Numeric) && from > to
602
+ @from = from
603
+ @to = to
604
+ end
462
605
 
463
- def instance?(o)
464
- o.is_a?(Numeric) && o >= @from && o <= @to
465
- end
606
+ # Checks if this numeric range intersects with another
607
+ #
608
+ # @param o [PNumericType] the range to compare with
609
+ # @return [Boolean] `true` if this range intersects with the other range
610
+ # @api public
611
+ def intersect?(o)
612
+ self.class == o.class && !(@to < o.from || o.to < @from)
613
+ end
466
614
 
467
- def unbounded?
468
- @from == -Float::INFINITY && @to == Float::INFINITY
469
- end
615
+ # Returns the lower bound of the numeric range or `nil` if no lower bound is set.
616
+ # @return [Float,Integer]
617
+ def from
618
+ @from == -Float::INFINITY ? nil : @from
619
+ end
470
620
 
471
- DEFAULT = PNumericType.new(-Float::INFINITY)
621
+ # Returns the upper bound of the numeric range or `nil` if no upper bound is set.
622
+ # @return [Float,Integer]
623
+ def to
624
+ @to == Float::INFINITY ? nil : @to
625
+ end
472
626
 
473
- protected
627
+ # Same as #from but will return `-Float::Infinity` instead of `nil` if no lower bound is set.
628
+ # @return [Float,Integer]
629
+ def numeric_from
630
+ @from
631
+ end
474
632
 
475
- # @api_private
476
- def _assignable?(o)
477
- return false unless o.is_a?(self.class)
478
- # If o min and max are within the range of t
479
- @from <= o.numeric_from && @to >= o.numeric_to
480
- end
481
- end
633
+ # Same as #to but will return `Float::Infinity` instead of `nil` if no lower bound is set.
634
+ # @return [Float,Integer]
635
+ def numeric_to
636
+ @to
637
+ end
482
638
 
483
- # @api public
484
- #
485
- class PIntegerType < PNumericType
486
- # The integer type is enumerable when it defines a range
487
- include Enumerable
639
+ def hash
640
+ @from.hash * 31 + @to.hash
641
+ end
488
642
 
489
- def enumerable?
490
- @from != -Float::INFINITY && @to != Float::INFINITY
491
- end
643
+ def eql?(o)
644
+ self.class == o.class && @from == o.numeric_from && @to == o.numeric_to
645
+ end
492
646
 
493
- def generalize
494
- DEFAULT
495
- end
647
+ def instance?(o)
648
+ o.is_a?(Numeric) && o >= @from && o <= @to
649
+ end
496
650
 
497
- def instance?(o)
498
- o.is_a?(Integer) && o >= numeric_from && o <= numeric_to
499
- end
651
+ def unbounded?
652
+ @from == -Float::INFINITY && @to == Float::INFINITY
653
+ end
500
654
 
501
- # Returns Float.Infinity if one end of the range is unbound
502
- def size
503
- return Float::INFINITY if @from == -Float::INFINITY || @to == Float::INFINITY
504
- 1+(to-from).abs
505
- end
655
+ DEFAULT = PNumericType.new(-Float::INFINITY)
506
656
 
507
- # Returns the range as an array ordered so the smaller number is always first.
508
- # The number may be Infinity or -Infinity.
509
- def range
510
- [@from, @to]
511
- end
657
+ protected
512
658
 
513
- # Returns Enumerator if no block is given
514
- # Returns nil if size is infinity (does not yield)
515
- def each
516
- if block_given?
517
- enumerable? ? @from.upto(@to) { |x| yield x } : nil
518
- else
519
- to_enum
520
- end
521
- end
659
+ # @api_private
660
+ def _assignable?(o, guard)
661
+ return false unless o.is_a?(self.class)
662
+ # If o min and max are within the range of t
663
+ @from <= o.numeric_from && @to >= o.numeric_to
664
+ end
665
+ end
522
666
 
523
- # Returns a range where both to and from are positive numbers. Negative
524
- # numbers are converted to zero
525
- # @return [PIntegerType] a positive range
526
- def to_size
527
- @from >= 0 ? self : PIntegerType.new(0, @to < 0 ? 0 : @to)
528
- end
667
+ # @api public
668
+ #
669
+ class PIntegerType < PNumericType
670
+ # Will respond `true` for any range that is bounded at both ends.
671
+ #
672
+ # @return [Boolean] `true` if the type describes a finite range.
673
+ def finite_range?
674
+ @from != -Float::INFINITY && @to != Float::INFINITY
675
+ end
529
676
 
530
- DEFAULT = PIntegerType.new(-Float::INFINITY)
531
- end
677
+ def generalize
678
+ DEFAULT
679
+ end
532
680
 
533
- # @api public
534
- #
535
- class PFloatType < PNumericType
536
- def generalize
537
- DEFAULT
538
- end
681
+ def instance?(o)
682
+ o.is_a?(Integer) && o >= numeric_from && o <= numeric_to
683
+ end
539
684
 
540
- def instance?(o)
541
- o.is_a?(Float) && o >= numeric_from && o <= numeric_to
542
- end
685
+ # Checks if this range is adjacent to the given range
686
+ #
687
+ # @param o [PIntegerType] the range to compare with
688
+ # @return [Boolean] `true` if this range is adjacent to the other range
689
+ # @api public
690
+ def adjacent?(o)
691
+ o.is_a?(PIntegerType) && (@to + 1 == o.from || o.to + 1 == @from)
692
+ end
543
693
 
544
- DEFAULT = PFloatType.new(-Float::INFINITY)
694
+ # Concatenates this range with another range provided that the ranges intersect or
695
+ # are adjacent. When that's not the case, this method will return `nil`
696
+ #
697
+ # @param o [PIntegerType] the range to concatenate with this range
698
+ # @return [PIntegerType,nil] the concatenated range or `nil` when the ranges were apart
699
+ # @api public
700
+ def merge(o)
701
+ if intersect?(o) || adjacent?(o)
702
+ min = @from <= o.from ? @from : o.from
703
+ max = @to >= o.to ? @to : o.to
704
+ PIntegerType.new(min, max)
705
+ else
706
+ nil
545
707
  end
708
+ end
546
709
 
547
- # @api public
548
- #
549
- class PCollectionType < PAnyType
550
- attr_reader :element_type, :size_type
551
-
552
- def initialize(element_type, size_type = nil)
553
- @element_type = element_type
554
- @size_type = size_type
555
- end
710
+ def iterable?(guard = nil)
711
+ true
712
+ end
556
713
 
557
- def generalize
558
- @element_type.nil? ? DEFAULT : PCollectionType.new(element_type.generalize, nil)
559
- end
714
+ def iterable_type(guard = nil)
715
+ # It's unknown if the iterable will be a range (min, max) or a "times" (0, max)
716
+ PIterableType.new(PIntegerType::DEFAULT)
717
+ end
560
718
 
561
- def instance?(o)
562
- assignable?(TypeCalculator.infer(o))
563
- end
719
+ # Returns Float.Infinity if one end of the range is unbound
720
+ def size
721
+ return Float::INFINITY if @from == -Float::INFINITY || @to == Float::INFINITY
722
+ 1+(to-from).abs
723
+ end
564
724
 
565
- # Returns an array with from (min) size to (max) size
566
- def size_range
567
- (@size_type || DEFAULT_SIZE).range
568
- end
725
+ # Returns the range as an array ordered so the smaller number is always first.
726
+ # The number may be Infinity or -Infinity.
727
+ def range
728
+ [@from, @to]
729
+ end
569
730
 
570
- def hash
571
- @element_type.hash * 31 + @size_type.hash
572
- end
731
+ # Returns Enumerator if no block is given
732
+ # Returns nil if size is infinity (does not yield)
733
+ def each(&block)
734
+ r = Iterable.on(self)
735
+ block_given? ? r.each(&block) : r
736
+ end
573
737
 
574
- def ==(o)
575
- self.class == o.class && @element_type == o.element_type && @size_type == o.size_type
576
- end
738
+ # Returns a range where both to and from are positive numbers. Negative
739
+ # numbers are converted to zero
740
+ # @return [PIntegerType] a positive range
741
+ def to_size
742
+ @from >= 0 ? self : PIntegerType.new(0, @to < 0 ? 0 : @to)
743
+ end
577
744
 
745
+ DEFAULT = PIntegerType.new(-Float::INFINITY)
746
+ end
578
747
 
579
- DEFAULT_SIZE = PIntegerType.new(0)
580
- ZERO_SIZE = PIntegerType.new(0, 0)
581
- DEFAULT = PCollectionType.new(nil)
748
+ # @api public
749
+ #
750
+ class PFloatType < PNumericType
751
+ def generalize
752
+ DEFAULT
753
+ end
582
754
 
583
- protected
755
+ def instance?(o)
756
+ o.is_a?(Float) && o >= numeric_from && o <= numeric_to
757
+ end
584
758
 
585
- # @api private
586
- #
587
- def _assignable?(o)
588
- case o
589
- when PCollectionType
590
- (@size_type || DEFAULT_SIZE).assignable?(o.size_type || DEFAULT_SIZE)
591
- when PTupleType
592
- # compute the tuple's min/max size, and check if that size matches
593
- from, to = type_to_range(o.size_type)
594
- from = o.types.size - 1 + from
595
- to = o.types.size - 1 + to
596
- (@size_type || DEFAULT_SIZE).assignable?(PIntegerType.new(from, to))
597
- when PStructType
598
- from = to = o.elements.size
599
- (@size_type || DEFAULT_SIZE).assignable?(PIntegerType.new(from, to))
600
- else
601
- false
602
- end
603
- end
759
+ # Concatenates this range with another range provided that the ranges intersect. When that's not the case, this
760
+ # method will return `nil`
761
+ #
762
+ # @param o [PFloatType] the range to concatenate with this range
763
+ # @return [PFloatType,nil] the concatenated range or `nil` when the ranges were apart
764
+ # @api public
765
+ def merge(o)
766
+ if intersect?(o)
767
+ min = @from <= o.from ? @from : o.from
768
+ max = @to >= o.to ? @to : o.to
769
+ PFloatType.new(min, max)
770
+ else
771
+ nil
604
772
  end
773
+ end
605
774
 
606
- # @api public
607
- #
608
- class PStringType < PScalarType
609
- attr_reader :size_type, :values
775
+ DEFAULT = PFloatType.new(-Float::INFINITY)
776
+ end
610
777
 
611
- def generalize
612
- DEFAULT
613
- end
778
+ # @api public
779
+ #
780
+ class PCollectionType < PAnyType
781
+ attr_reader :element_type, :size_type
614
782
 
615
- def initialize(size_type, values = [])
616
- @size_type = size_type
617
- @values = values.sort.freeze
618
- end
783
+ def initialize(element_type, size_type = nil)
784
+ @element_type = element_type
785
+ @size_type = size_type
786
+ end
619
787
 
620
- def hash
621
- @size_type.hash * 31 + @values.hash
622
- end
788
+ def accept(visitor, guard)
789
+ super
790
+ @size_type.accept(visitor, guard) unless @size_type.nil?
791
+ @element_type.accept(visitor, guard) unless @element_type.nil?
792
+ end
623
793
 
624
- def ==(o)
625
- self.class == o.class && @size_type == o.size_type && @values == o.values
626
- end
794
+ def generalize
795
+ if @element_type.nil?
796
+ DEFAULT
797
+ else
798
+ ge_type = @element_type.generalize
799
+ @size_type.nil? && @element_type.equal?(ge_type) ? self : self.class.new(ge_type, nil)
800
+ end
801
+ end
627
802
 
628
- def instance?(o)
629
- # true if size compliant
630
- if o.is_a?(String) && (@size_type.nil? || @size_type.instance?(o.size))
631
- @values.empty? || @values.include?(o)
632
- else
633
- false
634
- end
635
- end
636
-
637
- DEFAULT = PStringType.new(nil)
638
- NON_EMPTY = PStringType.new(PIntegerType.new(1))
639
-
640
- protected
641
-
642
- # @api private
643
- def _assignable?(o)
644
- if values.empty?
645
- # A general string is assignable by any other string or pattern restricted string
646
- # if the string has a size constraint it does not match since there is no reasonable way
647
- # to compute the min/max length a pattern will match. For enum, it is possible to test that
648
- # each enumerator value is within range
649
- case o
650
- when PStringType
651
- # true if size compliant
652
- (@size_type || PCollectionType::DEFAULT_SIZE).assignable?(o.size_type || PCollectionType::DEFAULT_SIZE)
653
-
654
- when PPatternType
655
- # true if size constraint is at least 0 to +Infinity (which is the same as the default)
656
- @size_type.nil? || @size_type.assignable?(PCollectionType::DEFAULT_SIZE)
657
-
658
- when PEnumType
659
- if o.values.empty?
660
- # enum represents all enums, and thus all strings, a sized constrained string can thus not
661
- # be assigned any enum (unless it is max size).
662
- @size_type.nil? || @size_type.assignable?(PCollectionType::DEFAULT_SIZE)
663
- else
664
- # true if all enum values are within range
665
- orange = o.values.map(&:size).minmax
666
- srange = (@size_type || PCollectionType::DEFAULT_SIZE).range
667
- # If o min and max are within the range of t
668
- srange[0] <= orange[0] && srange[1] >= orange[1]
669
- end
670
- else
671
- # no other type matches string
672
- false
673
- end
674
- elsif o.is_a?(PStringType)
675
- # A specific string acts as a set of strings - must have exactly the same strings
676
- # In this case, size does not matter since the definition is very precise anyway
677
- values == o.values
678
- else
679
- # All others are false, since no other type describes the same set of specific strings
680
- false
681
- end
682
- end
803
+ def normalize(guard = nil)
804
+ if @element_type.nil?
805
+ DEFAULT
806
+ else
807
+ ne_type = @element_type.normalize(guard)
808
+ @element_type.equal?(ne_type) ? self : self.class.new(ne_type, @size_type)
683
809
  end
810
+ end
684
811
 
685
- # @api public
686
- #
687
- class PRegexpType < PScalarType
688
- attr_reader :pattern
812
+ def instance?(o)
813
+ assignable?(TypeCalculator.infer(o))
814
+ end
689
815
 
690
- def initialize(pattern)
691
- @pattern = pattern
692
- end
816
+ # Returns an array with from (min) size to (max) size
817
+ def size_range
818
+ (@size_type || DEFAULT_SIZE).range
819
+ end
693
820
 
694
- def regexp
695
- @regexp ||= Regexp.new(@pattern || '')
696
- end
821
+ def hash
822
+ @element_type.hash * 31 + @size_type.hash
823
+ end
697
824
 
698
- def hash
699
- @pattern.hash
700
- end
825
+ def iterable?(guard = nil)
826
+ true
827
+ end
701
828
 
702
- def ==(o)
703
- self.class == o.class && @pattern == o.pattern
704
- end
829
+ def iterable_type(guard = nil)
830
+ @element_type.nil? ? PIterableType::DEFAULT : PIterableType.new(@element_type)
831
+ end
832
+
833
+ def eql?(o)
834
+ self.class == o.class && @element_type == o.element_type && @size_type == o.size_type
835
+ end
705
836
 
706
- DEFAULT = PRegexpType.new(nil)
707
837
 
708
- protected
838
+ DEFAULT_SIZE = PIntegerType.new(0)
839
+ ZERO_SIZE = PIntegerType.new(0, 0)
840
+ DEFAULT = PCollectionType.new(nil)
709
841
 
710
- # @api private
711
- #
712
- def _assignable?(o)
713
- o.is_a?(PRegexpType) && (@pattern.nil? || @pattern == o.pattern)
714
- end
842
+ protected
843
+
844
+ # @api private
845
+ #
846
+ def _assignable?(o, guard)
847
+ case o
848
+ when PCollectionType
849
+ (@size_type || DEFAULT_SIZE).assignable?(o.size_type || DEFAULT_SIZE, guard)
850
+ when PTupleType
851
+ # compute the tuple's min/max size, and check if that size matches
852
+ from, to = type_to_range(o.size_type)
853
+ from = o.types.size - 1 + from
854
+ to = o.types.size - 1 + to
855
+ (@size_type || DEFAULT_SIZE).assignable?(PIntegerType.new(from, to), guard)
856
+ when PStructType
857
+ from = to = o.elements.size
858
+ (@size_type || DEFAULT_SIZE).assignable?(PIntegerType.new(from, to), guard)
859
+ else
860
+ false
715
861
  end
862
+ end
863
+ end
716
864
 
717
- # Represents a subtype of String that narrows the string to those matching the patterns
718
- # If specified without a pattern it is basically the same as the String type.
719
- #
720
- # @api public
721
- #
722
- class PPatternType < PScalarType
723
- attr_reader :patterns
865
+ class PIterableType < PTypeWithContainedType
866
+ def element_type
867
+ @type
868
+ end
724
869
 
725
- def initialize(patterns)
726
- @patterns = patterns.freeze
870
+ def instance?(o)
871
+ if @type.nil? || @type.assignable?(PAnyType::DEFAULT)
872
+ # Any element_type will do
873
+ case o
874
+ when Iterable, String, Hash, Array, Range, PEnumType
875
+ true
876
+ when Integer
877
+ o >= 0
878
+ when PIntegerType
879
+ o.finite_range?
880
+ else
881
+ false
727
882
  end
883
+ else
884
+ assignable?(TypeCalculator.infer(o))
885
+ end
886
+ end
728
887
 
729
- def hash
730
- @patterns.hash
731
- end
888
+ def iterable?(guard = nil)
889
+ true
890
+ end
732
891
 
733
- def ==(o)
734
- self.class == o.class && (@patterns | o.patterns).size == @patterns.size
735
- end
892
+ def iterable_type(guard = nil)
893
+ self
894
+ end
736
895
 
737
- DEFAULT = PPatternType.new([])
896
+ DEFAULT = PIterableType.new(nil)
738
897
 
739
- protected
898
+ protected
740
899
 
741
- # @api private
742
- #
743
- def _assignable?(o)
744
- return true if self == o
745
- case o
746
- when PStringType, PEnumType
900
+ # @api private
901
+ def _assignable?(o, guard)
902
+ if @type.nil? || @type.assignable?(PAnyType::DEFAULT, guard)
903
+ # Don't request the iterable_type. Since this Iterable accepts Any element, it is enough that o is iterable.
904
+ o.iterable?
905
+ else
906
+ o = o.iterable_type
907
+ o.nil? || o.element_type.nil? ? false : @type.assignable?(o.element_type, guard)
908
+ end
909
+ end
910
+ end
911
+
912
+ # @api public
913
+ #
914
+ class PIteratorType < PTypeWithContainedType
915
+ def element_type
916
+ @type
917
+ end
918
+
919
+ def instance?(o)
920
+ o.is_a?(Iterable) && (@element_type.nil? || @element_type.assignable?(o.element_type))
921
+ end
922
+
923
+ def iterable?(guard = nil)
924
+ true
925
+ end
926
+
927
+ def iterable_type(guard = nil)
928
+ @type.nil? ? PIteratbleType::DEFAULT : PIterableType.new(@element_type)
929
+ end
930
+
931
+ DEFAULT = PIteratorType.new(nil)
932
+
933
+ protected
934
+
935
+ # @api private
936
+ def _assignable?(o, guard)
937
+ o.is_a?(PIteratorType) && (@element_type.nil? || @element_type.assignable?(o.element_type, guard))
938
+ end
939
+ end
940
+
941
+ # @api public
942
+ #
943
+ class PStringType < PScalarType
944
+ attr_reader :size_type, :values
945
+
946
+ def initialize(size_type, values = EMPTY_ARRAY)
947
+ @size_type = size_type
948
+ @values = values.sort.freeze
949
+ end
950
+
951
+ def accept(visitor, guard)
952
+ super
953
+ @size_type.accept(visitor, guard) unless @size_type.nil?
954
+ end
955
+
956
+ def generalize
957
+ DEFAULT
958
+ end
959
+
960
+ def hash
961
+ @size_type.hash * 31 + @values.hash
962
+ end
963
+
964
+ def iterable?(guard = nil)
965
+ true
966
+ end
967
+
968
+ def iterable_type(guard = nil)
969
+ ITERABLE_TYPE
970
+ end
971
+
972
+ def eql?(o)
973
+ self.class == o.class && @size_type == o.size_type && @values == o.values
974
+ end
975
+
976
+ def instance?(o)
977
+ # true if size compliant
978
+ if o.is_a?(String) && (@size_type.nil? || @size_type.instance?(o.size))
979
+ @values.empty? || @values.include?(o)
980
+ else
981
+ false
982
+ end
983
+ end
984
+
985
+ DEFAULT = PStringType.new(nil)
986
+ NON_EMPTY = PStringType.new(PIntegerType.new(1))
987
+
988
+ # Iterates over each character of the string
989
+ ITERABLE_TYPE = PIterableType.new(PStringType.new(PIntegerType.new(1,1)))
990
+
991
+ protected
992
+
993
+ # @api private
994
+ def _assignable?(o, guard)
995
+ if values.empty?
996
+ # A general string is assignable by any other string or pattern restricted string
997
+ # if the string has a size constraint it does not match since there is no reasonable way
998
+ # to compute the min/max length a pattern will match. For enum, it is possible to test that
999
+ # each enumerator value is within range
1000
+ case o
1001
+ when PStringType
1002
+ # true if size compliant
1003
+ (@size_type || PCollectionType::DEFAULT_SIZE).assignable?(
1004
+ o.size_type || PCollectionType::DEFAULT_SIZE, guard)
1005
+
1006
+ when PPatternType
1007
+ # true if size constraint is at least 0 to +Infinity (which is the same as the default)
1008
+ @size_type.nil? || @size_type.assignable?(PCollectionType::DEFAULT_SIZE, guard)
1009
+
1010
+ when PEnumType
747
1011
  if o.values.empty?
748
- # Strings / Enums (unknown which ones) cannot all match a pattern, but if there is no pattern it is ok
749
- # (There should really always be a pattern, but better safe than sorry).
750
- @patterns.empty?
1012
+ # enum represents all enums, and thus all strings, a sized constrained string can thus not
1013
+ # be assigned any enum (unless it is max size).
1014
+ @size_type.nil? || @size_type.assignable?(PCollectionType::DEFAULT_SIZE, guard)
751
1015
  else
752
- # all strings in String/Enum type must match one of the patterns in Pattern type,
753
- # or Pattern represents all Patterns == all Strings
754
- regexps = @patterns.map { |p| p.regexp }
755
- regexps.empty? || o.values.all? { |v| regexps.any? {|re| re.match(v) } }
1016
+ # true if all enum values are within range
1017
+ orange = o.values.map(&:size).minmax
1018
+ srange = (@size_type || PCollectionType::DEFAULT_SIZE).range
1019
+ # If o min and max are within the range of t
1020
+ srange[0] <= orange[0] && srange[1] >= orange[1]
756
1021
  end
757
- when PPatternType
758
- @patterns.empty?
759
1022
  else
1023
+ # no other type matches string
760
1024
  false
761
- end
762
1025
  end
1026
+ elsif o.is_a?(PStringType)
1027
+ # A specific string acts as a set of strings - must have exactly the same strings
1028
+ # In this case, size does not matter since the definition is very precise anyway
1029
+ values == o.values
1030
+ else
1031
+ # All others are false, since no other type describes the same set of specific strings
1032
+ false
763
1033
  end
1034
+ end
1035
+ end
764
1036
 
765
- # @api public
766
- #
767
- class PBooleanType < PScalarType
1037
+ # @api public
1038
+ #
1039
+ class PRegexpType < PScalarType
1040
+ attr_reader :pattern
768
1041
 
769
- def instance?(o)
770
- o == true || o == false
771
- end
1042
+ def initialize(pattern)
1043
+ @pattern = pattern
1044
+ end
772
1045
 
773
- DEFAULT = PBooleanType.new
1046
+ def regexp
1047
+ @regexp ||= Regexp.new(@pattern || '')
1048
+ end
774
1049
 
775
- protected
1050
+ def hash
1051
+ @pattern.hash
1052
+ end
776
1053
 
777
- # @api private
778
- #
779
- def _assignable?(o)
780
- o.is_a?(PBooleanType)
781
- end
1054
+ def eql?(o)
1055
+ self.class == o.class && @pattern == o.pattern
1056
+ end
1057
+
1058
+ DEFAULT = PRegexpType.new(nil)
1059
+
1060
+ protected
1061
+
1062
+ # @api private
1063
+ #
1064
+ def _assignable?(o, guard)
1065
+ o.is_a?(PRegexpType) && (@pattern.nil? || @pattern == o.pattern)
1066
+ end
1067
+ end
1068
+
1069
+ # Represents a subtype of String that narrows the string to those matching the patterns
1070
+ # If specified without a pattern it is basically the same as the String type.
1071
+ #
1072
+ # @api public
1073
+ #
1074
+ class PPatternType < PScalarType
1075
+ attr_reader :patterns
1076
+
1077
+ def initialize(patterns)
1078
+ @patterns = patterns.freeze
1079
+ end
1080
+
1081
+ def accept(visitor, guard)
1082
+ super
1083
+ @patterns.each { |p| p.accept(visitor, guard) }
1084
+ end
1085
+
1086
+ def hash
1087
+ @patterns.hash
1088
+ end
1089
+
1090
+ def eql?(o)
1091
+ self.class == o.class && @patterns.size == o.patterns.size && (@patterns - o.patterns).empty?
1092
+ end
1093
+
1094
+ DEFAULT = PPatternType.new(EMPTY_ARRAY)
1095
+
1096
+ protected
1097
+
1098
+ # @api private
1099
+ #
1100
+ def _assignable?(o, guard)
1101
+ return true if self == o
1102
+ case o
1103
+ when PStringType, PEnumType
1104
+ if o.values.empty?
1105
+ # Strings / Enums (unknown which ones) cannot all match a pattern, but if there is no pattern it is ok
1106
+ # (There should really always be a pattern, but better safe than sorry).
1107
+ @patterns.empty?
1108
+ else
1109
+ # all strings in String/Enum type must match one of the patterns in Pattern type,
1110
+ # or Pattern represents all Patterns == all Strings
1111
+ regexps = @patterns.map { |p| p.regexp }
1112
+ regexps.empty? || o.values.all? { |v| regexps.any? {|re| re.match(v) } }
1113
+ end
1114
+ when PPatternType
1115
+ @patterns.empty?
1116
+ else
1117
+ false
782
1118
  end
1119
+ end
1120
+ end
783
1121
 
784
- # @api public
785
- #
786
- # @api public
787
- #
788
- class PStructElement < TypedModelObject
789
- attr_accessor :key_type, :value_type
1122
+ # @api public
1123
+ #
1124
+ class PBooleanType < PScalarType
790
1125
 
791
- def hash
792
- value_type.hash * 31 + key_type.hash
793
- end
1126
+ def instance?(o)
1127
+ o == true || o == false
1128
+ end
794
1129
 
795
- def name
796
- k = key_type
797
- k = k.optional_type if k.is_a?(POptionalType)
798
- k.values[0]
799
- end
1130
+ DEFAULT = PBooleanType.new
800
1131
 
801
- def initialize(key_type, value_type)
802
- @key_type = key_type
803
- @value_type = value_type
804
- end
1132
+ protected
805
1133
 
806
- def generalize
807
- PStructElement.new(@key_type, @value_type.generalize)
808
- end
1134
+ # @api private
1135
+ #
1136
+ def _assignable?(o, guard)
1137
+ o.is_a?(PBooleanType)
1138
+ end
1139
+ end
809
1140
 
810
- def <=>(o)
811
- self.name <=> o.name
812
- end
1141
+ # @api public
1142
+ #
1143
+ # @api public
1144
+ #
1145
+ class PStructElement < TypedModelObject
1146
+ attr_accessor :key_type, :value_type
813
1147
 
814
- def ==(o)
815
- self.class == o.class && value_type == o.value_type && key_type == o.key_type
816
- end
1148
+ def accept(visitor, guard)
1149
+ @key_type.accept(visitor, guard)
1150
+ @value_type.accept(visitor, guard)
1151
+ end
1152
+
1153
+ def hash
1154
+ value_type.hash * 31 + key_type.hash
1155
+ end
1156
+
1157
+ def name
1158
+ k = key_type
1159
+ k = k.optional_type if k.is_a?(POptionalType)
1160
+ k.values[0]
1161
+ end
1162
+
1163
+ def initialize(key_type, value_type)
1164
+ @key_type = key_type
1165
+ @value_type = value_type
1166
+ end
1167
+
1168
+ def generalize
1169
+ gv_type = @value_type.generalize
1170
+ @value_type.equal?(gv_type) ? self : PStructElement.new(@key_type, gv_type)
1171
+ end
1172
+
1173
+ def normalize(guard = nil)
1174
+ nv_type = @value_type.normalize(guard)
1175
+ @value_type.equal?(nv_type) ? self : PStructElement.new(@key_type, nv_type)
1176
+ end
1177
+
1178
+ def <=>(o)
1179
+ self.name <=> o.name
1180
+ end
1181
+
1182
+ def eql?(o)
1183
+ self.class == o.class && value_type == o.value_type && key_type == o.key_type
1184
+ end
1185
+ end
1186
+
1187
+ # @api public
1188
+ #
1189
+ class PStructType < PAnyType
1190
+ include Enumerable
1191
+
1192
+ def initialize(elements)
1193
+ @elements = elements.sort.freeze
1194
+ end
1195
+
1196
+ def accept(visitor, guard)
1197
+ super
1198
+ @elements.each { |elem| elem.accept(visitor, guard) }
1199
+ end
1200
+
1201
+ def each
1202
+ if block_given?
1203
+ elements.each { |elem| yield elem }
1204
+ else
1205
+ elements.to_enum
817
1206
  end
1207
+ end
818
1208
 
819
- # @api public
820
- #
821
- class PStructType < PAnyType
822
- include Enumerable
1209
+ def generalize
1210
+ if @elements.empty?
1211
+ DEFAULT
1212
+ else
1213
+ alter_type_array(@elements, :generalize) { |altered| PStructType.new(altered) }
1214
+ end
1215
+ end
823
1216
 
824
- def initialize(elements)
825
- @elements = elements.sort.freeze
826
- end
1217
+ def normalize(guard = nil)
1218
+ if @elements.empty?
1219
+ DEFAULT
1220
+ else
1221
+ alter_type_array(@elements, :normalize, guard) { |altered| PStructType.new(altered) }
1222
+ end
1223
+ end
827
1224
 
828
- def each
829
- if block_given?
830
- elements.each { |elem| yield elem }
831
- else
832
- elements.to_enum
833
- end
834
- end
1225
+ def hashed_elements
1226
+ @hashed ||= @elements.reduce({}) {|memo, e| memo[e.name] = e; memo }
1227
+ end
835
1228
 
836
- def generalize
837
- @elements.empty? ? DEFAULT : PStructType.new(@elements.map { |se| se.generalize })
838
- end
1229
+ def hash
1230
+ @elements.hash
1231
+ end
839
1232
 
840
- def hashed_elements
841
- @hashed ||= @elements.reduce({}) {|memo, e| memo[e.name] = e; memo }
842
- end
843
-
844
- def hash
845
- @elements.hash
846
- end
1233
+ def iterable?(guard = nil)
1234
+ true
1235
+ end
847
1236
 
848
- def ==(o)
849
- self.class == o.class && @elements == o.elements
850
- end
1237
+ def iterable_type(guard = nil)
1238
+ if self == DEFAULT
1239
+ PIterableType.new(PHashType::DEFAULT_KEY_PAIR_TUPLE)
1240
+ else
1241
+ tc = TypeCalculator.singleton
1242
+ PIterableType.new(
1243
+ PTupleType.new([
1244
+ tc.unwrap_single_variant(PVariantType.new(@elements.map {|se| se.key_type })),
1245
+ tc.unwrap_single_variant(PVariantType.new(@elements.map {|se| se.value_type }))],
1246
+ PHashType::KEY_PAIR_TUPLE_SIZE))
1247
+ end
1248
+ end
851
1249
 
852
- def elements
853
- @elements
854
- end
1250
+ def eql?(o)
1251
+ self.class == o.class && @elements == o.elements
1252
+ end
855
1253
 
856
- def instance?(o)
857
- return false unless o.is_a?(Hash)
858
- matched = 0
859
- @elements.all? do |e|
860
- key = e.name
861
- v = o[key]
862
- if v.nil? && !o.include?(key)
863
- # Entry is missing. Only OK when key is optional
864
- e.key_type.assignable?(PUndefType::DEFAULT)
865
- else
866
- matched += 1
867
- e.value_type.instance?(v)
868
- end
869
- end && matched == o.size
870
- end
1254
+ def elements
1255
+ @elements
1256
+ end
871
1257
 
872
- DEFAULT = PStructType.new([])
873
-
874
- protected
875
-
876
- # @api private
877
- def _assignable?(o)
878
- if o.is_a?(Types::PStructType)
879
- h2 = o.hashed_elements
880
- matched = 0
881
- elements.all? do |e1|
882
- e2 = h2[e1.name]
883
- if e2.nil?
884
- e1.key_type.assignable?(PUndefType::DEFAULT)
885
- else
886
- matched += 1
887
- e1.key_type.assignable?(e2.key_type) && e1.value_type.assignable?(e2.value_type)
888
- end
889
- end && matched == h2.size
890
- elsif o.is_a?(Types::PHashType)
891
- required = 0
892
- required_elements_assignable = elements.all? do |e|
893
- if e.key_type.assignable?(PUndefType::DEFAULT)
894
- true
895
- else
896
- required += 1
897
- e.value_type.assignable?(o.element_type)
898
- end
899
- end
900
- if required_elements_assignable
901
- size_o = o.size_type || collection_default_size_t
902
- PIntegerType.new(required, elements.size).assignable?(size_o)
903
- end
1258
+ def instance?(o)
1259
+ return false unless o.is_a?(Hash)
1260
+ matched = 0
1261
+ @elements.all? do |e|
1262
+ key = e.name
1263
+ v = o[key]
1264
+ if v.nil? && !o.include?(key)
1265
+ # Entry is missing. Only OK when key is optional
1266
+ e.key_type.assignable?(PUndefType::DEFAULT)
1267
+ else
1268
+ matched += 1
1269
+ e.value_type.instance?(v)
1270
+ end
1271
+ end && matched == o.size
1272
+ end
1273
+
1274
+ DEFAULT = PStructType.new(EMPTY_ARRAY)
1275
+
1276
+ protected
1277
+
1278
+ # @api private
1279
+ def _assignable?(o, guard)
1280
+ if o.is_a?(Types::PStructType)
1281
+ h2 = o.hashed_elements
1282
+ matched = 0
1283
+ elements.all? do |e1|
1284
+ e2 = h2[e1.name]
1285
+ if e2.nil?
1286
+ e1.key_type.assignable?(PUndefType::DEFAULT, guard)
904
1287
  else
905
- false
1288
+ matched += 1
1289
+ e1.key_type.assignable?(e2.key_type, guard) && e1.value_type.assignable?(e2.value_type, guard)
1290
+ end
1291
+ end && matched == h2.size
1292
+ elsif o.is_a?(Types::PHashType)
1293
+ required = 0
1294
+ required_elements_assignable = elements.all? do |e|
1295
+ if e.key_type.assignable?(PUndefType::DEFAULT)
1296
+ true
1297
+ else
1298
+ required += 1
1299
+ e.value_type.assignable?(o.element_type, guard)
906
1300
  end
907
1301
  end
1302
+ if required_elements_assignable
1303
+ size_o = o.size_type || collection_default_size_t
1304
+ PIntegerType.new(required, elements.size).assignable?(size_o, guard)
1305
+ end
1306
+ else
1307
+ false
908
1308
  end
1309
+ end
1310
+ end
909
1311
 
910
- # @api public
911
- #
912
- class PTupleType < PAnyType
913
- include Enumerable
1312
+ # @api public
1313
+ #
1314
+ class PTupleType < PAnyType
1315
+ include Enumerable
914
1316
 
915
- # If set, describes min and max required of the given types - if max > size of
916
- # types, the last type entry repeats
917
- #
918
- attr_reader :size_type
1317
+ # If set, describes min and max required of the given types - if max > size of
1318
+ # types, the last type entry repeats
1319
+ #
1320
+ attr_reader :size_type
919
1321
 
920
- attr_reader :types
1322
+ attr_reader :types
921
1323
 
922
- # @api private
923
- def callable_args?(callable_t)
924
- unless size_type.nil?
925
- raise ArgumentError, 'Callable tuple may not have a size constraint when used as args'
926
- end
1324
+ def accept(visitor, guard)
1325
+ super
1326
+ @size_type.accept(visitor, guard) unless @size_type.nil?
1327
+ @types.each { |elem| elem.accept(visitor, guard) }
1328
+ end
927
1329
 
928
- params_tuple = callable_t.param_types
929
- param_block_t = callable_t.block_type
930
- arg_types = @types
931
- arg_block_t = arg_types.last
932
- if arg_block_t.kind_of_callable?
933
- # Can't pass a block to a callable that doesn't accept one
934
- return false if param_block_t.nil?
935
-
936
- # Check that the block is of the right tyṕe
937
- return false unless param_block_t.assignable?(arg_block_t)
938
-
939
- # Check other arguments
940
- arg_count = arg_types.size - 1
941
- params_size_t = params_tuple.size_type || PIntegerType.new(*params_tuple.size_range)
942
- return false unless params_size_t.assignable?(PIntegerType.new(arg_count, arg_count))
943
-
944
- ctypes = params_tuple.types
945
- arg_count.times do |index|
946
- return false unless (ctypes[index] || ctypes[-1]).assignable?(arg_types[index])
947
- end
948
- return true
949
- end
1330
+ # @api private
1331
+ def callable_args?(callable_t, guard)
1332
+ unless size_type.nil?
1333
+ raise ArgumentError, 'Callable tuple may not have a size constraint when used as args'
1334
+ end
950
1335
 
951
- # Check that tuple is assignable and that the block (if declared) is optional
952
- params_tuple.assignable?(self) && (param_block_t.nil? || param_block_t.assignable?(PUndefType::DEFAULT))
953
- end
1336
+ params_tuple = callable_t.param_types
1337
+ param_block_t = callable_t.block_type
1338
+ arg_types = @types
1339
+ arg_block_t = arg_types.last
1340
+ if arg_block_t.kind_of_callable?(true, guard)
1341
+ # Can't pass a block to a callable that doesn't accept one
1342
+ return false if param_block_t.nil?
954
1343
 
955
- def initialize(types, size_type = nil)
956
- @types = types
957
- @size_type = size_type.nil? ? nil : size_type.to_size
958
- end
1344
+ # Check that the block is of the right tyṕe
1345
+ return false unless param_block_t.assignable?(arg_block_t, guard)
959
1346
 
960
- # Returns Enumerator for the types if no block is given, otherwise, calls the given
961
- # block with each of the types in this tuple
962
- def each
963
- if block_given?
964
- types.each { |x| yield x }
965
- else
966
- types.to_enum
967
- end
968
- end
1347
+ # Check other arguments
1348
+ arg_count = arg_types.size - 1
1349
+ params_size_t = params_tuple.size_type || PIntegerType.new(*params_tuple.size_range)
1350
+ return false unless params_size_t.assignable?(PIntegerType.new(arg_count, arg_count), guard)
969
1351
 
970
- def generalize
971
- self == DEFAULT ? self : PTupleType.new(@types.map {|t| t.generalize })
1352
+ ctypes = params_tuple.types
1353
+ arg_count.times do |index|
1354
+ return false unless (ctypes[index] || ctypes[-1]).assignable?(arg_types[index], guard)
972
1355
  end
1356
+ return true
1357
+ end
973
1358
 
974
- def instance?(o)
975
- return false unless o.is_a?(Array)
976
- # compute the tuple's min/max size, and check if that size matches
977
- size_t = size_type || PIntegerType.new(*size_range)
1359
+ # Check that tuple is assignable and that the block (if declared) is optional
1360
+ params_tuple.assignable?(self, guard) && (param_block_t.nil? || param_block_t.assignable?(PUndefType::DEFAULT, guard))
1361
+ end
978
1362
 
979
- return false unless size_t.instance?(o.size)
980
- o.each_with_index do |element, index|
981
- return false unless (types[index] || types[-1]).instance?(element)
982
- end
983
- true
984
- end
1363
+ def initialize(types, size_type = nil)
1364
+ @types = types
1365
+ @size_type = size_type.nil? ? nil : size_type.to_size
1366
+ end
985
1367
 
986
- # Returns the number of elements accepted [min, max] in the tuple
987
- def size_range
988
- if @size_type.nil?
989
- types_size = @types.size
990
- [types_size, types_size]
991
- else
992
- @size_type.range
993
- end
994
- end
1368
+ # Returns Enumerator for the types if no block is given, otherwise, calls the given
1369
+ # block with each of the types in this tuple
1370
+ def each
1371
+ if block_given?
1372
+ types.each { |x| yield x }
1373
+ else
1374
+ types.to_enum
1375
+ end
1376
+ end
995
1377
 
996
- # Returns the number of accepted occurrences [min, max] of the last type in the tuple
997
- # The defaults is [1,1]
998
- #
999
- def repeat_last_range
1000
- if @size_type.nil?
1001
- return [1, 1]
1002
- end
1003
- types_size = @types.size
1004
- from, to = @size_type.range
1005
- min = from - (types_size-1)
1006
- min = min <= 0 ? 0 : min
1007
- max = to - (types_size-1)
1008
- [min, max]
1009
- end
1378
+ def generalize
1379
+ if self == DEFAULT
1380
+ DEFAULT
1381
+ else
1382
+ alter_type_array(@types, :generalize) { |altered_types| PTupleType.new(altered_types, @size_type) }
1383
+ end
1384
+ end
1010
1385
 
1011
- def hash
1012
- @size_type.hash * 31 + @types.hash
1013
- end
1386
+ def normalize(guard = nil)
1387
+ if self == DEFAULT
1388
+ DEFAULT
1389
+ else
1390
+ alter_type_array(@types, :normalize, guard) { |altered_types| PTupleType.new(altered_types, @size_type) }
1391
+ end
1392
+ end
1014
1393
 
1015
- def ==(o)
1016
- self.class == o.class && @types == o.types && @size_type == o.size_type
1017
- end
1394
+ def instance?(o)
1395
+ return false unless o.is_a?(Array)
1396
+ # compute the tuple's min/max size, and check if that size matches
1397
+ size_t = size_type || PIntegerType.new(*size_range)
1018
1398
 
1019
- DATA = PTupleType.new([PDataType::DEFAULT], PCollectionType::DEFAULT_SIZE)
1020
- DEFAULT = PTupleType.new([])
1399
+ return false unless size_t.instance?(o.size)
1400
+ o.each_with_index do |element, index|
1401
+ return false unless (types[index] || types[-1]).instance?(element)
1402
+ end
1403
+ true
1404
+ end
1021
1405
 
1022
- protected
1406
+ def iterable?(guard = nil)
1407
+ true
1408
+ end
1023
1409
 
1024
- # @api private
1025
- def _assignable?(o)
1026
- return true if self == o
1027
- s_types = types
1028
- return true if s_types.empty? && (o.is_a?(PArrayType))
1029
- size_s = size_type || PIntegerType.new(*size_range)
1410
+ def iterable_type(guard = nil)
1411
+ PIterableType.new(TypeCalculator.singleton.unwrap_single_variant(PVariantType.new(types)))
1412
+ end
1030
1413
 
1031
- if o.is_a?(PTupleType)
1032
- size_o = o.size_type || PIntegerType.new(*o.size_range)
1414
+ # Returns the number of elements accepted [min, max] in the tuple
1415
+ def size_range
1416
+ if @size_type.nil?
1417
+ types_size = @types.size
1418
+ [types_size, types_size]
1419
+ else
1420
+ @size_type.range
1421
+ end
1422
+ end
1033
1423
 
1034
- # not assignable if the number of types in o is outside number of types in t1
1035
- if size_s.assignable?(size_o)
1036
- o_types = o.types
1037
- o_types.size.times do |index|
1038
- return false unless (s_types[index] || s_types[-1]).assignable?(o_types[index])
1039
- end
1040
- return true
1041
- else
1042
- return false
1043
- end
1044
- elsif o.is_a?(PArrayType)
1045
- o_entry = o.element_type
1046
- # Array of anything can not be assigned (unless tuple is tuple of anything) - this case
1047
- # was handled at the top of this method.
1048
- #
1049
- return false if o_entry.nil?
1050
- size_o = o.size_type || PCollectionType::DEFAULT_SIZE
1051
- return false unless size_s.assignable?(size_o)
1052
- [s_types.size, size_o.range[1]].min.times { |index| return false unless (s_types[index] || s_types[-1]).assignable?(o_entry) }
1053
- true
1054
- else
1055
- false
1056
- end
1057
- end
1424
+ # Returns the number of accepted occurrences [min, max] of the last type in the tuple
1425
+ # The defaults is [1,1]
1426
+ #
1427
+ def repeat_last_range
1428
+ if @size_type.nil?
1429
+ return [1, 1]
1058
1430
  end
1431
+ types_size = @types.size
1432
+ from, to = @size_type.range
1433
+ min = from - (types_size-1)
1434
+ min = min <= 0 ? 0 : min
1435
+ max = to - (types_size-1)
1436
+ [min, max]
1437
+ end
1059
1438
 
1060
- # @api public
1061
- #
1062
- class PCallableType < PAnyType
1063
- # Types of parameters as a Tuple with required/optional count, or an Integer with min (required), max count
1064
- # @return [PTupleType] the tuple representing the parameter types
1065
- attr_reader :param_types
1066
-
1067
- # Although being an abstract type reference, only Callable, or all Callables wrapped in
1068
- # Optional or Variant are supported
1069
- # If not set, the meaning is that block is not supported.
1070
- # @return [PAnyType|nil] the block type
1071
- attr_reader :block_type
1072
-
1073
- # @param param_types [PTupleType]
1074
- # @param block_type [PAnyType|nil]
1075
- def initialize(param_types, block_type = nil)
1076
- @param_types = param_types
1077
- @block_type = block_type
1078
- end
1439
+ def hash
1440
+ @size_type.hash * 31 + @types.hash
1441
+ end
1079
1442
 
1080
- def generalize
1081
- return self if self == DEFAULT
1082
- params_t = @param_types.nil? ? nil : @param_types.generalize
1083
- block_t = @block_type.nil? ? nil : @block_type.generalize
1084
- PCallableType.new(params_t, block_t)
1085
- end
1443
+ def eql?(o)
1444
+ self.class == o.class && @types == o.types && @size_type == o.size_type
1445
+ end
1086
1446
 
1087
- def instance?(o)
1088
- assignable?(TypeCalculator.infer(o))
1089
- end
1447
+ DATA = PTupleType.new([PDataType::DEFAULT], PCollectionType::DEFAULT_SIZE)
1448
+ DEFAULT = PTupleType.new(EMPTY_ARRAY)
1090
1449
 
1091
- # @api private
1092
- def callable_args?(required_callable_t)
1093
- # If the required callable is euqal or more specific than self, self is acceptable arguments
1094
- required_callable_t.assignable?(self)
1095
- end
1450
+ protected
1096
1451
 
1097
- def kind_of_callable?(optional=true)
1098
- true
1099
- end
1452
+ # @api private
1453
+ def _assignable?(o, guard)
1454
+ return true if self == o
1455
+ s_types = types
1456
+ return true if s_types.empty? && (o.is_a?(PArrayType))
1457
+ size_s = size_type || PIntegerType.new(*size_range)
1100
1458
 
1101
- # Returns the number of accepted arguments [min, max]
1102
- def size_range
1103
- @param_types.nil? ? nil : @param_types.size_range
1104
- end
1459
+ if o.is_a?(PTupleType)
1460
+ size_o = o.size_type || PIntegerType.new(*o.size_range)
1105
1461
 
1106
- # Returns the number of accepted arguments for the last parameter type [min, max]
1462
+ # not assignable if the number of types in o is outside number of types in t1
1463
+ if size_s.assignable?(size_o, guard)
1464
+ o_types = o.types
1465
+ o_types.size.times do |index|
1466
+ return false unless (s_types[index] || s_types[-1]).assignable?(o_types[index], guard)
1467
+ end
1468
+ return true
1469
+ else
1470
+ return false
1471
+ end
1472
+ elsif o.is_a?(PArrayType)
1473
+ o_entry = o.element_type
1474
+ # Array of anything can not be assigned (unless tuple is tuple of anything) - this case
1475
+ # was handled at the top of this method.
1107
1476
  #
1108
- def last_range
1109
- @param_types.nil? ? nil : @param_types.repeat_last_range
1110
- end
1477
+ return false if o_entry.nil?
1478
+ size_o = o.size_type || PCollectionType::DEFAULT_SIZE
1479
+ return false unless size_s.assignable?(size_o, guard)
1480
+ [s_types.size, size_o.range[1]].min.times { |index| return false unless (s_types[index] || s_types[-1]).assignable?(o_entry, guard) }
1481
+ true
1482
+ else
1483
+ false
1484
+ end
1485
+ end
1486
+ end
1111
1487
 
1112
- # Range [0,0], [0,1], or [1,1] for the block
1113
- #
1114
- def block_range
1115
- case block_type
1116
- when POptionalType
1117
- [0,1]
1118
- when PVariantType, PCallableType
1119
- [1,1]
1120
- else
1121
- [0,0]
1122
- end
1123
- end
1488
+ # @api public
1489
+ #
1490
+ class PCallableType < PAnyType
1491
+ # Types of parameters as a Tuple with required/optional count, or an Integer with min (required), max count
1492
+ # @return [PTupleType] the tuple representing the parameter types
1493
+ attr_reader :param_types
1494
+
1495
+ # Although being an abstract type reference, only Callable, or all Callables wrapped in
1496
+ # Optional or Variant are supported
1497
+ # If not set, the meaning is that block is not supported.
1498
+ # @return [PAnyType|nil] the block type
1499
+ attr_reader :block_type
1500
+
1501
+ # @param param_types [PTupleType]
1502
+ # @param block_type [PAnyType|nil]
1503
+ def initialize(param_types, block_type = nil)
1504
+ @param_types = param_types
1505
+ @block_type = block_type
1506
+ end
1124
1507
 
1125
- def hash
1126
- @param_types.hash * 31 + @block_type.hash
1127
- end
1508
+ def accept(visitor, guard)
1509
+ super
1510
+ @param_types.accept(visitor, guard) unless @param_types.nil?
1511
+ @block_type.accept(visitor, guard) unless @block_type.nil?
1512
+ end
1128
1513
 
1129
- def ==(o)
1130
- self.class == o.class && @param_types == o.param_types && @block_type == o.block_type
1131
- end
1514
+ def generalize
1515
+ if self == DEFAULT
1516
+ DEFAULT
1517
+ else
1518
+ params_t = @param_types.nil? ? nil : @param_types.generalize
1519
+ block_t = @block_type.nil? ? nil : @block_type.generalize
1520
+ @param_types.equal?(params_t) && @block_type.equal?(block_t) ? self : PCallableType.new(params_t, block_t)
1521
+ end
1522
+ end
1523
+
1524
+ def normalize(guard = nil)
1525
+ if self == DEFAULT
1526
+ DEFAULT
1527
+ else
1528
+ params_t = @param_types.nil? ? nil : @param_types.normalize(guard)
1529
+ block_t = @block_type.nil? ? nil : @block_type.normalize(guard)
1530
+ @param_types.equal?(params_t) && @block_type.equal?(block_t) ? self : PCallableType.new(params_t, block_t)
1531
+ end
1532
+ end
1533
+
1534
+ def instance?(o)
1535
+ assignable?(TypeCalculator.infer(o))
1536
+ end
1132
1537
 
1133
- DEFAULT = PCallableType.new(nil)
1538
+ # @api private
1539
+ def callable_args?(required_callable_t, guard)
1540
+ # If the required callable is euqal or more specific than self, self is acceptable arguments
1541
+ required_callable_t.assignable?(self, guard)
1542
+ end
1134
1543
 
1135
- protected
1544
+ def kind_of_callable?(optional=true, guard = nil)
1545
+ true
1546
+ end
1136
1547
 
1137
- # @api private
1138
- def _assignable?(o)
1139
- return false unless o.is_a?(PCallableType)
1140
- # nil param_types means, any other Callable is assignable
1141
- return true if @param_types.nil?
1548
+ # Returns the number of accepted arguments [min, max]
1549
+ def size_range
1550
+ @param_types.nil? ? nil : @param_types.size_range
1551
+ end
1142
1552
 
1143
- # NOTE: these tests are made in reverse as it is calling the callable that is constrained
1144
- # (it's lower bound), not its upper bound
1145
- return false unless o.param_types.assignable?(@param_types)
1146
- # names are ignored, they are just information
1147
- # Blocks must be compatible
1148
- this_block_t = @block_type || PUndefType::DEFAULT
1149
- that_block_t = o.block_type || PUndefType::DEFAULT
1150
- that_block_t.assignable?(this_block_t)
1151
- end
1553
+ # Returns the number of accepted arguments for the last parameter type [min, max]
1554
+ #
1555
+ def last_range
1556
+ @param_types.nil? ? nil : @param_types.repeat_last_range
1557
+ end
1558
+
1559
+ # Range [0,0], [0,1], or [1,1] for the block
1560
+ #
1561
+ def block_range
1562
+ case block_type
1563
+ when POptionalType
1564
+ [0,1]
1565
+ when PVariantType, PCallableType
1566
+ [1,1]
1567
+ else
1568
+ [0,0]
1152
1569
  end
1570
+ end
1153
1571
 
1154
- # @api public
1155
- #
1156
- class PArrayType < PCollectionType
1572
+ def hash
1573
+ @param_types.hash * 31 + @block_type.hash
1574
+ end
1157
1575
 
1158
- # @api private
1159
- def callable_args?(callable)
1160
- param_t = callable.param_types
1161
- block_t = callable.block_type
1162
- # does not support calling with a block, but have to check that callable is ok with missing block
1163
- (param_t.nil? || param_t.assignable?(self)) && (block_t.nil? || block_t.assignable(PUndefType::DEFAULT))
1164
- end
1576
+ def eql?(o)
1577
+ self.class == o.class && @param_types == o.param_types && @block_type == o.block_type
1578
+ end
1165
1579
 
1166
- def generalize
1167
- if self == DEFAULT
1168
- self
1169
- else
1170
- PArrayType.new(element_type.nil? ? nil : element_type.generalize)
1171
- end
1172
- end
1580
+ DEFAULT = PCallableType.new(nil)
1173
1581
 
1174
- def instance?(o)
1175
- return false unless o.is_a?(Array)
1176
- element_t = element_type
1177
- return false unless element_t.nil? || o.all? {|element| element_t.instance?(element) }
1178
- size_t = size_type
1179
- size_t.nil? || size_t.instance?(o.size)
1180
- end
1582
+ protected
1181
1583
 
1182
- DATA = PArrayType.new(PDataType::DEFAULT, PCollectionType::DEFAULT_SIZE)
1183
- DEFAULT = PArrayType.new(nil)
1184
- EMPTY = PArrayType.new(PUnitType::DEFAULT, PCollectionType::ZERO_SIZE)
1185
-
1186
- protected
1187
-
1188
- # Array is assignable if o is an Array and o's element type is assignable, or if o is a Tuple
1189
- # @api private
1190
- def _assignable?(o)
1191
- s_entry = element_type
1192
- if o.is_a?(PTupleType)
1193
-
1194
- # Tuple of anything can not be assigned (unless array is tuple of anything) - this case
1195
- # was handled at the top of this method.
1196
- #
1197
- return false if s_entry.nil?
1198
-
1199
- return false unless o.types.all? {|o_element_t| s_entry.assignable?(o_element_t) }
1200
- o_regular = o.types[0..-2]
1201
- o_ranged = o.types[-1]
1202
- o_from, o_to = type_to_range(o.size_type)
1203
- o_required = o_regular.size + o_from
1204
-
1205
- # array type may be size constrained
1206
- size_s = size_type || DEFAULT_SIZE
1207
- min, max = size_s.range
1208
- # Tuple with fewer min entries can not be assigned
1209
- return false if o_required < min
1210
- # Tuple with more optionally available entries can not be assigned
1211
- return false if o_regular.size + o_to > max
1212
- # each tuple type must be assignable to the element type
1213
- o_required.times do |index|
1214
- o_entry = tuple_entry_at(o, o_from, o_to, index)
1215
- return false unless s_entry.assignable?(o_entry)
1216
- end
1217
- # ... and so must the last, possibly optional (ranged) type
1218
- s_entry.assignable?(o_ranged)
1219
- elsif o.is_a?(PArrayType)
1220
- super && (s_entry.nil? || s_entry.assignable?(o.element_type))
1221
- else
1222
- false
1223
- end
1224
- end
1584
+ # @api private
1585
+ def _assignable?(o, guard)
1586
+ return false unless o.is_a?(PCallableType)
1587
+ # nil param_types means, any other Callable is assignable
1588
+ return true if @param_types.nil?
1589
+
1590
+ # NOTE: these tests are made in reverse as it is calling the callable that is constrained
1591
+ # (it's lower bound), not its upper bound
1592
+ return false unless o.param_types.assignable?(@param_types, guard)
1593
+ # names are ignored, they are just information
1594
+ # Blocks must be compatible
1595
+ this_block_t = @block_type || PUndefType::DEFAULT
1596
+ that_block_t = o.block_type || PUndefType::DEFAULT
1597
+ that_block_t.assignable?(this_block_t, guard)
1598
+ end
1599
+ end
1600
+
1601
+ # @api public
1602
+ #
1603
+ class PArrayType < PCollectionType
1604
+
1605
+ # @api private
1606
+ def callable_args?(callable, guard = nil)
1607
+ param_t = callable.param_types
1608
+ block_t = callable.block_type
1609
+ # does not support calling with a block, but have to check that callable is ok with missing block
1610
+ (param_t.nil? || param_t.assignable?(self, guard)) && (block_t.nil? || block_t.assignable(PUndefType::DEFAULT, guard))
1611
+ end
1612
+
1613
+ def generalize
1614
+ if self == DATA
1615
+ self
1616
+ else
1617
+ super
1225
1618
  end
1619
+ end
1226
1620
 
1227
- # @api public
1228
- #
1229
- class PHashType < PCollectionType
1230
- attr_accessor :key_type
1621
+ def normalize(guard = nil)
1622
+ if self == DATA
1623
+ self
1624
+ else
1625
+ super
1626
+ end
1627
+ end
1231
1628
 
1232
- def initialize(key_type, value_type, size_type = nil)
1233
- super(value_type, size_type)
1234
- @key_type = key_type
1235
- end
1629
+ def instance?(o)
1630
+ return false unless o.is_a?(Array)
1631
+ element_t = element_type
1632
+ return false unless element_t.nil? || o.all? {|element| element_t.instance?(element) }
1633
+ size_t = size_type
1634
+ size_t.nil? || size_t.instance?(o.size)
1635
+ end
1236
1636
 
1237
- def generalize
1238
- if self == DEFAULT || self == EMPTY
1239
- self
1240
- else
1241
- key_t = @key_type
1242
- key_t = key_t.generalize unless key_t.nil?
1243
- value_t = element_type
1244
- value_t = value_t.generalize unless value_t.nil?
1245
- PHashType.new(key_t, value_t)
1246
- end
1247
- end
1637
+ DATA = PArrayType.new(PDataType::DEFAULT, DEFAULT_SIZE)
1638
+ DEFAULT = PArrayType.new(nil)
1639
+ EMPTY = PArrayType.new(PUnitType::DEFAULT, ZERO_SIZE)
1640
+
1641
+ protected
1642
+
1643
+ # Array is assignable if o is an Array and o's element type is assignable, or if o is a Tuple
1644
+ # @api private
1645
+ def _assignable?(o, guard)
1646
+ s_entry = element_type
1647
+ if o.is_a?(PTupleType)
1648
+ # If s_entry is nil, this Array type has no opinion on element types. Therefore any
1649
+ # tuple can be assigned.
1650
+ return true if s_entry.nil?
1651
+
1652
+ return false unless o.types.all? {|o_element_t| s_entry.assignable?(o_element_t, guard) }
1653
+ o_regular = o.types[0..-2]
1654
+ o_ranged = o.types[-1]
1655
+ o_from, o_to = type_to_range(o.size_type)
1656
+ o_required = o_regular.size + o_from
1657
+
1658
+ # array type may be size constrained
1659
+ size_s = size_type || DEFAULT_SIZE
1660
+ min, max = size_s.range
1661
+ # Tuple with fewer min entries can not be assigned
1662
+ return false if o_required < min
1663
+ # Tuple with more optionally available entries can not be assigned
1664
+ return false if o_regular.size + o_to > max
1665
+ # each tuple type must be assignable to the element type
1666
+ o_required.times do |index|
1667
+ o_entry = tuple_entry_at(o, o_from, o_to, index)
1668
+ return false unless s_entry.assignable?(o_entry, guard)
1669
+ end
1670
+ # ... and so must the last, possibly optional (ranged) type
1671
+ s_entry.assignable?(o_ranged)
1672
+ elsif o.is_a?(PArrayType)
1673
+ super && (s_entry.nil? || s_entry.assignable?(o.element_type, guard))
1674
+ else
1675
+ false
1676
+ end
1677
+ end
1678
+ end
1248
1679
 
1249
- def hash
1250
- @key_type.hash * 31 + super
1251
- end
1680
+ # @api public
1681
+ #
1682
+ class PHashType < PCollectionType
1683
+ attr_accessor :key_type
1252
1684
 
1253
- def instance?(o)
1254
- return false unless o.is_a?(Hash)
1255
- key_t = key_type
1256
- element_t = element_type
1257
- if (key_t.nil? || o.keys.all? {|key| key_t.instance?(key) }) &&
1258
- (element_t.nil? || o.values.all? {|value| element_t.instance?(value) })
1259
- size_t = size_type
1260
- size_t.nil? || size_t.instance?(o.size)
1261
- else
1262
- false
1263
- end
1264
- end
1685
+ def initialize(key_type, value_type, size_type = nil)
1686
+ super(value_type, size_type)
1687
+ @key_type = key_type
1688
+ end
1265
1689
 
1266
- def ==(o)
1267
- super && @key_type == o.key_type
1268
- end
1690
+ def accept(visitor, guard)
1691
+ super
1692
+ @key_type.accept(visitor, guard) unless @key_type.nil?
1693
+ end
1269
1694
 
1270
- def is_the_empty_hash?
1271
- self == EMPTY
1272
- end
1695
+ def generalize
1696
+ if self == DEFAULT || self == DATA || self == EMPTY
1697
+ self
1698
+ else
1699
+ key_t = @key_type
1700
+ key_t = key_t.generalize unless key_t.nil?
1701
+ value_t = @element_type
1702
+ value_t = value_t.generalize unless value_t.nil?
1703
+ @size_type.nil? && @key_type.equal?(key_t) && @element_type.equal?(value_t) ? self : PHashType.new(key_t, value_t, nil)
1704
+ end
1705
+ end
1706
+
1707
+ def normalize(guard = nil)
1708
+ if self == DEFAULT || self == DATA || self == EMPTY
1709
+ self
1710
+ else
1711
+ key_t = @key_type
1712
+ key_t = key_t.normalize(guard) unless key_t.nil?
1713
+ value_t = @element_type
1714
+ value_t = value_t.normalize(guard) unless value_t.nil?
1715
+ @size_type.nil? && @key_type.equal?(key_t) && @element_type.equal?(value_t) ? self : PHashType.new(key_t, value_t, nil)
1716
+ end
1717
+ end
1718
+
1719
+ def hash
1720
+ @key_type.hash * 31 + super
1721
+ end
1722
+
1723
+ def instance?(o)
1724
+ return false unless o.is_a?(Hash)
1725
+ key_t = key_type
1726
+ element_t = element_type
1727
+ if (key_t.nil? || o.keys.all? {|key| key_t.instance?(key) }) &&
1728
+ (element_t.nil? || o.values.all? {|value| element_t.instance?(value) })
1729
+ size_t = size_type
1730
+ size_t.nil? || size_t.instance?(o.size)
1731
+ else
1732
+ false
1733
+ end
1734
+ end
1735
+
1736
+ def iterable?(guard = nil)
1737
+ true
1738
+ end
1739
+
1740
+ def iterable_type(guard = nil)
1741
+ if self == DEFAULT || self == EMPTY
1742
+ PIterableType.new(DEFAULT_KEY_PAIR_TUPLE)
1743
+ else
1744
+ PIterableType.new(PTupleType.new([@key_type, @element_type], KEY_PAIR_TUPLE_SIZE))
1745
+ end
1746
+ end
1747
+
1748
+ def eql?(o)
1749
+ super && @key_type == o.key_type
1750
+ end
1751
+
1752
+ def is_the_empty_hash?
1753
+ self == EMPTY
1754
+ end
1755
+
1756
+ DEFAULT = PHashType.new(nil, nil)
1757
+ KEY_PAIR_TUPLE_SIZE = PIntegerType.new(2,2)
1758
+ DEFAULT_KEY_PAIR_TUPLE = PTupleType.new([PUnitType::DEFAULT, PUnitType::DEFAULT], KEY_PAIR_TUPLE_SIZE)
1759
+ DATA = PHashType.new(PScalarType::DEFAULT, PDataType::DEFAULT, DEFAULT_SIZE)
1760
+ EMPTY = PHashType.new(PUnitType::DEFAULT, PUnitType::DEFAULT, PIntegerType.new(0, 0))
1761
+
1762
+ protected
1763
+
1764
+ # Hash is assignable if o is a Hash and o's key and element types are assignable
1765
+ # @api private
1766
+ def _assignable?(o, guard)
1767
+ case o
1768
+ when PHashType
1769
+ size_s = size_type
1770
+ return true if (size_s.nil? || size_s.from == 0) && o.is_the_empty_hash?
1771
+ return false unless (key_type.nil? || key_type.assignable?(o.key_type, guard)) && (element_type.nil? || element_type.assignable?(o.element_type, guard))
1772
+ super
1773
+ when PStructType
1774
+ # hash must accept String as key type
1775
+ # hash must accept all value types
1776
+ # hash must accept the size of the struct
1777
+ o_elements = o.elements
1778
+ (size_type || DEFAULT_SIZE).instance?(o_elements.size) &&
1779
+ o_elements.all? {|e| (key_type.nil? || key_type.instance?(e.name)) && (element_type.nil? || element_type.assignable?(e.value_type, guard)) }
1780
+ else
1781
+ false
1782
+ end
1783
+ end
1784
+ end
1785
+
1786
+ # A flexible type describing an any? of other types
1787
+ # @api public
1788
+ #
1789
+ class PVariantType < PAnyType
1790
+ include Enumerable
1273
1791
 
1274
- DEFAULT = PHashType.new(nil, nil)
1275
- DATA = PHashType.new(PScalarType::DEFAULT, PDataType::DEFAULT, DEFAULT_SIZE)
1276
- EMPTY = PHashType.new(PUndefType::DEFAULT, PUndefType::DEFAULT, PIntegerType.new(0, 0))
1277
-
1278
- protected
1279
-
1280
- # Hash is assignable if o is a Hash and o's key and element types are assignable
1281
- # @api private
1282
- def _assignable?(o)
1283
- case o
1284
- when PHashType
1285
- size_s = size_type
1286
- return true if (size_s.nil? || size_s.from == 0) && o.is_the_empty_hash?
1287
- return false unless (key_type.nil? || key_type.assignable?(o.key_type)) && (element_type.nil? || element_type.assignable?(o.element_type))
1288
- super
1289
- when PStructType
1290
- # hash must accept String as key type
1291
- # hash must accept all value types
1292
- # hash must accept the size of the struct
1293
- o_elements = o.elements
1294
- (size_type || DEFAULT_SIZE).instance?(o_elements.size) &&
1295
- o_elements.all? {|e| (key_type.nil? || key_type.instance?(e.name)) && (element_type.nil? || element_type.assignable?(e.value_type)) }
1792
+ attr_reader :types
1793
+
1794
+ # @param types [Array[PAnyType]] the variants
1795
+ def initialize(types)
1796
+ @types = types.uniq.freeze
1797
+ end
1798
+
1799
+ def accept(visitor, guard)
1800
+ super
1801
+ @types.each { |t| t.accept(visitor, guard) }
1802
+ end
1803
+
1804
+ def each
1805
+ if block_given?
1806
+ types.each { |t| yield t }
1807
+ else
1808
+ types.to_enum
1809
+ end
1810
+ end
1811
+
1812
+ def generalize
1813
+ if self == DEFAULT || self == DATA
1814
+ self
1815
+ else
1816
+ alter_type_array(@types, :generalize) { |altered| PVariantType.new(mod_types) }
1817
+ end
1818
+ end
1819
+
1820
+ def normalize(guard = nil)
1821
+ if self == DEFAULT || self == DATA || @types.empty?
1822
+ self
1823
+ else
1824
+ # Normalize all contained types
1825
+ modified = false
1826
+ types = alter_type_array(@types, :normalize, guard)
1827
+ if types == self
1828
+ types = @types
1829
+ else
1830
+ modified = true
1831
+ end
1832
+
1833
+ if types.size == 1
1834
+ types[0]
1835
+ elsif types.any? { |t| t.is_a?(PUndefType) }
1836
+ # Undef entry present. Use an OptionalType with a normalized Variant of all types that are not Undef
1837
+ POptionalType.new(PVariantType.new(types.reject { |ot| ot.is_a?(PUndefType) }).normalize(guard)).normalize(guard)
1838
+ else
1839
+ # Merge all variants into this one
1840
+ types = types.map do |t|
1841
+ if t.is_a?(PVariantType)
1842
+ modified = true
1843
+ t.types
1296
1844
  else
1297
- false
1845
+ t
1846
+ end
1847
+ end
1848
+ types.flatten! if modified
1849
+ size_before_merge = types.size
1850
+
1851
+ types = swap_not_undefs(types)
1852
+ types = swap_optionals(types)
1853
+ types = merge_enums(types)
1854
+ types = merge_patterns(types)
1855
+ types = merge_int_ranges(types)
1856
+ types = merge_float_ranges(types)
1857
+
1858
+ if types.size == 1
1859
+ types[0]
1860
+ else
1861
+ modified || types.size != size_before_merge ? PVariantType.new(types) : self
1298
1862
  end
1299
1863
  end
1300
1864
  end
1865
+ end
1301
1866
 
1302
- # A flexible type describing an any? of other types
1303
- # @api public
1304
- #
1305
- class PVariantType < PAnyType
1306
- include Enumerable
1867
+ def hash
1868
+ @types.hash
1869
+ end
1307
1870
 
1308
- attr_reader :types
1871
+ def instance?(o)
1872
+ # instance of variant if o is instance? of any of variant's types
1873
+ @types.any? { |type| type.instance?(o) }
1874
+ end
1309
1875
 
1310
- def initialize(types)
1311
- @types = types.freeze
1312
- end
1876
+ def kind_of_callable?(optional = true, guard = nil)
1877
+ @types.all? { |type| type.kind_of_callable?(optional, guard) }
1878
+ end
1879
+
1880
+ def resolved?
1881
+ @types.all? { |type| type.resolved? }
1882
+ end
1883
+
1884
+ def eql?(o)
1885
+ o = DATA if o.is_a?(PDataType)
1886
+ self.class == o.class && @types.size == o.types.size && (@types - o.types).empty?
1887
+ end
1313
1888
 
1314
- def each
1315
- if block_given?
1316
- types.each { |t| yield t }
1889
+ # Variant compatible with the Data type.
1890
+ DATA = PVariantType.new([PHashType::DATA, PArrayType::DATA, PScalarType::DEFAULT, PUndefType::DEFAULT, PTupleType::DATA])
1891
+
1892
+ DEFAULT = PVariantType.new(EMPTY_ARRAY)
1893
+
1894
+ protected
1895
+
1896
+ # @api private
1897
+ def _assignable?(o, guard)
1898
+ # Data is a specific variant
1899
+ o = DATA if o.is_a?(PDataType)
1900
+ if o.is_a?(PVariantType)
1901
+ # A variant is assignable if all of its options are assignable to one of this type's options
1902
+ return true if self == o
1903
+ o.types.all? do |other|
1904
+ # if the other is a Variant, all of its options, but be assignable to one of this type's options
1905
+ other = other.is_a?(PDataType) ? DATA : other
1906
+ if other.is_a?(PVariantType)
1907
+ assignable?(other, guard)
1317
1908
  else
1318
- types.to_enum
1909
+ types.any? {|option_t| option_t.assignable?(other, guard) }
1319
1910
  end
1320
1911
  end
1912
+ else
1913
+ # A variant is assignable if o is assignable to any of its types
1914
+ types.any? { |option_t| option_t.assignable?(o, guard) }
1915
+ end
1916
+ end
1321
1917
 
1322
- def generalize
1323
- (self == DEFAULT || self == DATA) ? self : PVariantType.new(@types.map {|t| t.generalize})
1918
+ # @api private
1919
+ def swap_optionals(array)
1920
+ if array.size > 1
1921
+ parts = array.partition {|t| t.is_a?(POptionalType) }
1922
+ optionals = parts[0]
1923
+ if optionals.size > 1
1924
+ others = parts[1]
1925
+ others << POptionalType.new(PVariantType.new(optionals.map { |optional| optional.type }).normalize)
1926
+ array = others
1324
1927
  end
1928
+ end
1929
+ array
1930
+ end
1325
1931
 
1326
- def hash
1327
- @types.hash
1932
+ # @api private
1933
+ def swap_not_undefs(array)
1934
+ if array.size > 1
1935
+ parts = array.partition {|t| t.is_a?(PNotUndefType) }
1936
+ not_undefs = parts[0]
1937
+ if not_undefs.size > 1
1938
+ others = parts[1]
1939
+ others << PNotUndefType.new(PVariantType.new(not_undefs.map { |not_undef| not_undef.type }).normalize)
1940
+ array = others
1328
1941
  end
1942
+ end
1943
+ array
1944
+ end
1329
1945
 
1330
- def instance?(o)
1331
- # instance of variant if o is instance? of any of variant's types
1332
- @types.any? { |type| type.instance?(o) }
1946
+ # @api private
1947
+ def merge_enums(array)
1948
+ if array.size > 1
1949
+ parts = array.partition {|t| t.is_a?(PEnumType) || t.is_a?(PStringType) && !t.values.empty? }
1950
+ enums = parts[0]
1951
+ if enums.size > 1
1952
+ others = parts[1]
1953
+ others << PEnumType.new(enums.map { |enum| enum.values }.flatten.uniq)
1954
+ array = others
1333
1955
  end
1956
+ end
1957
+ array
1958
+ end
1334
1959
 
1335
- def kind_of_callable?(optional = true)
1336
- @types.all? { |type| type.kind_of_callable?(optional) }
1960
+ # @api private
1961
+ def merge_patterns(array)
1962
+ if array.size > 1
1963
+ parts = array.partition {|t| t.is_a?(PPatternType) }
1964
+ patterns = parts[0]
1965
+ if patterns.size > 1
1966
+ others = parts[1]
1967
+ others << PPatternType.new(patterns.map { |pattern| pattern.patterns }.flatten.uniq)
1968
+ array = others
1337
1969
  end
1970
+ end
1971
+ array
1972
+ end
1338
1973
 
1339
- def ==(o)
1340
- # TODO: This special case doesn't look like it belongs here
1341
- self.class == o.class && (@types | o.types).size == @types.size ||
1342
- o.class == PDataType && self == DATA
1343
- end
1974
+ # @api private
1975
+ def merge_int_ranges(array)
1976
+ if array.size > 1
1977
+ parts = array.partition {|t| t.is_a?(PIntegerType) }
1978
+ ranges = parts[0]
1979
+ array = merge_ranges(ranges) + parts[1] if ranges.size > 1
1980
+ end
1981
+ array
1982
+ end
1344
1983
 
1345
- # Variant compatible with the Data type.
1346
- DATA = PVariantType.new([PHashType::DATA, PArrayType::DATA, PScalarType::DEFAULT, PUndefType::DEFAULT, PTupleType::DATA])
1347
-
1348
- DEFAULT = PVariantType.new([])
1349
-
1350
- protected
1351
-
1352
- # @api private
1353
- def _assignable?(o)
1354
- # Data is a specific variant
1355
- o = DATA if o.is_a?(PDataType)
1356
- if o.is_a?(PVariantType)
1357
- # A variant is assignable if all of its options are assignable to one of this type's options
1358
- return true if self == o
1359
- o.types.all? do |other|
1360
- # if the other is a Variant, all of its options, but be assignable to one of this type's options
1361
- other = other.is_a?(PDataType) ? DATA : other
1362
- if other.is_a?(PVariantType)
1363
- assignable?(other)
1364
- else
1365
- types.any? {|option_t| option_t.assignable?(other) }
1366
- end
1367
- end
1984
+ def merge_float_ranges(array)
1985
+ if array.size > 1
1986
+ parts = array.partition {|t| t.is_a?(PFloatType) }
1987
+ ranges = parts[0]
1988
+ array = merge_ranges(ranges) + parts[1] if ranges.size > 1
1989
+ end
1990
+ array
1991
+ end
1992
+
1993
+ # @api private
1994
+ def merge_ranges(ranges)
1995
+ result = []
1996
+ while !ranges.empty?
1997
+ unmerged = []
1998
+ x = ranges.pop
1999
+ result << ranges.inject(x) do |memo, y|
2000
+ merged = memo.merge(y)
2001
+ if merged.nil?
2002
+ unmerged << y
1368
2003
  else
1369
- # A variant is assignable if o is assignable to any of its types
1370
- types.any? { |option_t| option_t.assignable?(o) }
2004
+ memo = merged
1371
2005
  end
2006
+ memo
1372
2007
  end
2008
+ ranges = unmerged
1373
2009
  end
2010
+ result
2011
+ end
2012
+ end
1374
2013
 
1375
- # @api public
1376
- #
1377
- class PRuntimeType < PAnyType
1378
- attr_reader :runtime, :runtime_type_name
2014
+ # @api public
2015
+ #
2016
+ class PRuntimeType < PAnyType
2017
+ attr_reader :runtime, :runtime_type_name
1379
2018
 
1380
- def initialize(runtime, runtime_type_name = nil)
1381
- @runtime = runtime
1382
- @runtime_type_name = runtime_type_name
1383
- end
2019
+ def initialize(runtime, runtime_type_name = nil)
2020
+ @runtime = runtime
2021
+ @runtime_type_name = runtime_type_name
2022
+ end
1384
2023
 
1385
- def hash
1386
- @runtime.hash * 31 + @runtime_type_name.hash
1387
- end
2024
+ def hash
2025
+ @runtime.hash * 31 + @runtime_type_name.hash
2026
+ end
1388
2027
 
1389
- def ==(o)
1390
- self.class == o.class && @runtime == o.runtime && @runtime_type_name == o.runtime_type_name
1391
- end
2028
+ def eql?(o)
2029
+ self.class == o.class && @runtime == o.runtime && @runtime_type_name == o.runtime_type_name
2030
+ end
1392
2031
 
1393
- def instance?(o)
1394
- assignable?(TypeCalculator.infer(o))
1395
- end
2032
+ def instance?(o)
2033
+ assignable?(TypeCalculator.infer(o))
2034
+ end
1396
2035
 
1397
- DEFAULT = PRuntimeType.new(nil)
2036
+ def iterable?(guard = nil)
2037
+ c = class_from_string(@runtime_type_name)
2038
+ c.nil? ? false : c < Iterable
2039
+ end
1398
2040
 
1399
- protected
2041
+ def iterable_type(guard = nil)
2042
+ iterable?(guard) ? PIterableType.new(self) : nil
2043
+ end
1400
2044
 
1401
- # Assignable if o's has the same runtime and the runtime name resolves to
1402
- # a class that is the same or subclass of t1's resolved runtime type name
1403
- # @api private
1404
- def _assignable?(o)
1405
- return false unless o.is_a?(PRuntimeType)
1406
- return false unless @runtime == o.runtime
1407
- return true if @runtime_type_name.nil? # t1 is wider
1408
- return false if o.runtime_type_name.nil? # t1 not nil, so o can not be wider
2045
+ DEFAULT = PRuntimeType.new(nil)
1409
2046
 
1410
- # NOTE: This only supports Ruby, must change when/if the set of runtimes is expanded
1411
- c1 = class_from_string(@runtime_type_name)
1412
- c2 = class_from_string(o.runtime_type_name)
1413
- return false unless c1.is_a?(Module) && c2.is_a?(Module)
1414
- !!(c2 <= c1)
1415
- end
1416
- end
2047
+ protected
1417
2048
 
1418
- # Abstract representation of a type that can be placed in a Catalog.
1419
- # @api public
1420
- #
1421
- class PCatalogEntryType < PAnyType
2049
+ # Assignable if o's has the same runtime and the runtime name resolves to
2050
+ # a class that is the same or subclass of t1's resolved runtime type name
2051
+ # @api private
2052
+ def _assignable?(o, guard)
2053
+ return false unless o.is_a?(PRuntimeType)
2054
+ return false unless @runtime == o.runtime
2055
+ return true if @runtime_type_name.nil? # t1 is wider
2056
+ return false if o.runtime_type_name.nil? # t1 not nil, so o can not be wider
1422
2057
 
1423
- DEFAULT = PCatalogEntryType.new
2058
+ # NOTE: This only supports Ruby, must change when/if the set of runtimes is expanded
2059
+ c1 = class_from_string(@runtime_type_name)
2060
+ c2 = class_from_string(o.runtime_type_name)
2061
+ return false unless c1.is_a?(Module) && c2.is_a?(Module)
2062
+ !!(c2 <= c1)
2063
+ end
2064
+ end
1424
2065
 
1425
- def instance?(o)
1426
- assignable?(TypeCalculator.infer(o))
1427
- end
2066
+ # Abstract representation of a type that can be placed in a Catalog.
2067
+ # @api public
2068
+ #
2069
+ class PCatalogEntryType < PAnyType
1428
2070
 
1429
- protected
1430
- # @api private
1431
- def _assignable?(o)
1432
- o.is_a?(PCatalogEntryType)
1433
- end
1434
- end
2071
+ DEFAULT = PCatalogEntryType.new
1435
2072
 
1436
- # Represents a (host-) class in the Puppet Language.
1437
- # @api public
1438
- #
1439
- class PHostClassType < PCatalogEntryType
1440
- attr_reader :class_name
2073
+ def instance?(o)
2074
+ assignable?(TypeCalculator.infer(o))
2075
+ end
1441
2076
 
1442
- def initialize(class_name)
1443
- @class_name = class_name
1444
- end
2077
+ protected
2078
+ # @api private
2079
+ def _assignable?(o, guard)
2080
+ o.is_a?(PCatalogEntryType)
2081
+ end
2082
+ end
1445
2083
 
1446
- def hash
1447
- 11 * @class_name.hash
1448
- end
1449
- def ==(o)
1450
- self.class == o.class && @class_name == o.class_name
1451
- end
2084
+ # Represents a (host-) class in the Puppet Language.
2085
+ # @api public
2086
+ #
2087
+ class PHostClassType < PCatalogEntryType
2088
+ attr_reader :class_name
1452
2089
 
1453
- DEFAULT = PHostClassType.new(nil)
2090
+ def initialize(class_name)
2091
+ @class_name = class_name
2092
+ end
1454
2093
 
1455
- protected
2094
+ def hash
2095
+ 11 * @class_name.hash
2096
+ end
2097
+ def eql?(o)
2098
+ self.class == o.class && @class_name == o.class_name
2099
+ end
1456
2100
 
1457
- # @api private
1458
- def _assignable?(o)
1459
- return false unless o.is_a?(PHostClassType)
1460
- # Class = Class[name}, Class[name] != Class
1461
- return true if @class_name.nil?
1462
- # Class[name] = Class[name]
1463
- @class_name == o.class_name
1464
- end
1465
- end
2101
+ DEFAULT = PHostClassType.new(nil)
1466
2102
 
1467
- # Represents a Resource Type in the Puppet Language
1468
- # @api public
1469
- #
1470
- class PResourceType < PCatalogEntryType
1471
- attr_reader :type_name, :title
2103
+ protected
1472
2104
 
1473
- def initialize(type_name, title = nil)
1474
- @type_name = type_name
1475
- @title = title
1476
- end
2105
+ # @api private
2106
+ def _assignable?(o, guard)
2107
+ return false unless o.is_a?(PHostClassType)
2108
+ # Class = Class[name}, Class[name] != Class
2109
+ return true if @class_name.nil?
2110
+ # Class[name] = Class[name]
2111
+ @class_name == o.class_name
2112
+ end
2113
+ end
1477
2114
 
1478
- def hash
1479
- @type_name.hash * 31 + @title.hash
1480
- end
2115
+ # Represents a Resource Type in the Puppet Language
2116
+ # @api public
2117
+ #
2118
+ class PResourceType < PCatalogEntryType
2119
+ attr_reader :type_name, :title
1481
2120
 
1482
- def ==(o)
1483
- self.class == o.class && @type_name == o.type_name && @title == o.title
1484
- end
2121
+ def initialize(type_name, title = nil)
2122
+ @type_name = type_name
2123
+ @title = title
2124
+ end
2125
+
2126
+ def hash
2127
+ @type_name.hash * 31 + @title.hash
2128
+ end
2129
+
2130
+ def eql?(o)
2131
+ self.class == o.class && @type_name == o.type_name && @title == o.title
2132
+ end
2133
+
2134
+ DEFAULT = PResourceType.new(nil)
2135
+
2136
+ protected
2137
+
2138
+ # @api private
2139
+ def _assignable?(o, guard)
2140
+ return false unless o.is_a?(PResourceType)
2141
+ return true if @type_name.nil?
2142
+ return false if @type_name != o.type_name
2143
+ return true if @title.nil?
2144
+ @title == o.title
2145
+ end
2146
+ end
2147
+
2148
+ # Represents a type that accept PUndefType instead of the type parameter
2149
+ # required_type - is a short hand for Variant[T, Undef]
2150
+ # @api public
2151
+ #
2152
+ class POptionalType < PTypeWithContainedType
2153
+ def optional_type
2154
+ @type
2155
+ end
1485
2156
 
1486
- DEFAULT = PResourceType.new(nil)
2157
+ def kind_of_callable?(optional=true, guard = nil)
2158
+ optional && !@type.nil? && @type.kind_of_callable?(optional, guard)
2159
+ end
1487
2160
 
1488
- protected
2161
+ def instance?(o)
2162
+ PUndefType::DEFAULT.instance?(o) || (!@type.nil? && @type.instance?(o))
2163
+ end
1489
2164
 
1490
- # @api private
1491
- def _assignable?(o)
1492
- return false unless o.is_a?(PResourceType)
1493
- return true if @type_name.nil?
1494
- return false if @type_name != o.type_name
1495
- return true if @title.nil?
1496
- @title == o.title
2165
+ def normalize(guard = nil)
2166
+ n = super
2167
+ if n.type.nil?
2168
+ n
2169
+ else
2170
+ if n.type.is_a?(PNotUndefType)
2171
+ # No point in having an NotUndef in an Optional
2172
+ POptionalType.new(n.type.type).normalize
2173
+ elsif n.type.assignable?(PUndefType::DEFAULT)
2174
+ # THe type is Optional anyway, so it can be stripped of
2175
+ n.type
2176
+ else
2177
+ n
1497
2178
  end
1498
2179
  end
2180
+ end
1499
2181
 
1500
- # Represents a type that accept PUndefType instead of the type parameter
1501
- # required_type - is a short hand for Variant[T, Undef]
1502
- # @api public
1503
- #
1504
- class POptionalType < PAnyType
1505
- attr_reader :optional_type
2182
+ DEFAULT = POptionalType.new(nil)
1506
2183
 
1507
- def initialize(optional_type)
1508
- @optional_type = optional_type
1509
- end
2184
+ protected
1510
2185
 
1511
- def generalize
1512
- @optional_type.nil? ? self : PType.new(@optional_type.generalize)
1513
- end
2186
+ # @api private
2187
+ def _assignable?(o, guard)
2188
+ return true if o.is_a?(PUndefType)
2189
+ return true if @type.nil?
2190
+ if o.is_a?(POptionalType)
2191
+ @type.assignable?(o.optional_type, guard)
2192
+ else
2193
+ @type.assignable?(o, guard)
2194
+ end
2195
+ end
2196
+ end
1514
2197
 
1515
- def hash
1516
- 7 * @optional_type.hash
1517
- end
2198
+ class PTypeReferenceType < PAnyType
2199
+ attr_reader :name, :parameters
1518
2200
 
1519
- def kind_of_callable?(optional=true)
1520
- optional && !@optional_type.nil? && @optional_type.kind_of_callable?(optional)
1521
- end
2201
+ def initialize(name, parameters = nil)
2202
+ @name = name
2203
+ @parameters = parameters.nil? ? EMPTY_ARRAY : parameters
2204
+ end
1522
2205
 
1523
- def ==(o)
1524
- self.class == o.class && @optional_type == o.optional_type
1525
- end
2206
+ def callable?(args)
2207
+ false
2208
+ end
1526
2209
 
1527
- def instance?(o)
1528
- PUndefType::DEFAULT.instance?(o) || (!optional_type.nil? && optional_type.instance?(o))
1529
- end
2210
+ def instance?(o)
2211
+ false
2212
+ end
2213
+
2214
+ def hash
2215
+ @name.hash * 31 + @parameters.hash
2216
+ end
1530
2217
 
1531
- DEFAULT = POptionalType.new(nil)
2218
+ def eql?(o)
2219
+ super && o.name == @name && o.parameters == @parameters
2220
+ end
1532
2221
 
1533
- protected
2222
+ protected
1534
2223
 
1535
- # @api private
1536
- def _assignable?(o)
1537
- return true if o.is_a?(PUndefType)
1538
- return true if @optional_type.nil?
1539
- if o.is_a?(POptionalType)
1540
- @optional_type.assignable?(o.optional_type)
1541
- else
1542
- @optional_type.assignable?(o)
1543
- end
2224
+ def _assignable?(o, guard)
2225
+ # A type must be assignable to itself or a lot of unit tests will break
2226
+ o == self
2227
+ end
2228
+
2229
+ DEFAULT = PTypeReferenceType.new('UnresolvedReference')
2230
+ end
2231
+
2232
+ # Describes a named alias for another Type.
2233
+ # The alias is created with a name and an unresolved type expression. The type expression may
2234
+ # in turn contain other aliases (including the alias that contains it) which means that an alias
2235
+ # might contain self recursion. Whether or not that is the case is computed and remembered when the alias
2236
+ # is resolved since guarding against self recursive constructs is relatively expensive.
2237
+ #
2238
+ class PTypeAliasType < PAnyType
2239
+ attr_reader :name
2240
+
2241
+ # @param name [String] The name of the type
2242
+ # @param type_expr [Model::PopsObject] The expression that describes the aliased type
2243
+ # @param resolved_type [PAnyType] the resolve type (only used for the DEFAULT initialization)
2244
+ def initialize(name, type_expr, resolved_type = nil)
2245
+ @name = name
2246
+ @type_expr = type_expr
2247
+ @resolved_type = resolved_type
2248
+ @self_recursion = false
2249
+ end
2250
+
2251
+ # Returns the resolved type. The type must have been resolved by a call prior to calls to this
2252
+ # method or an error will be raised.
2253
+ #
2254
+ # @return [PAnyType] The resolved type of this alias.
2255
+ # @raise [Puppet::Error] unless the type has been resolved prior to calling this method
2256
+ def resolved_type
2257
+ raise Puppet::Error, "Reference to unresolved type #{@name}" unless @resolved_type
2258
+ @resolved_type
2259
+ end
2260
+
2261
+ def callable_args?(callable, guard)
2262
+ guarded_recursion(guard, false) { |g| resolved_type.callable_args?(callable, g) }
2263
+ end
2264
+
2265
+ def kind_of_callable?(optional=true, guard = nil)
2266
+ guarded_recursion(guard, false) { |g| resolved_type.kind_of_callable?(optional, g) }
2267
+ end
2268
+
2269
+ def instance?(o)
2270
+ # No value can ever be recursive so no guard is needed here
2271
+ resolved_type.instance?(o)
2272
+ end
2273
+
2274
+ def iterable?(guard = nil)
2275
+ guarded_recursion(guard, false) { |g| resolved_type.iterable?(g) }
2276
+ end
2277
+
2278
+ def iterable_type(guard = nil)
2279
+ guarded_recursion(guard, nil) { |g| resolved_type.iterable_type(g) }
2280
+ end
2281
+
2282
+ def hash
2283
+ @name.hash
2284
+ end
2285
+
2286
+ # Called from the TypeParser once it has found a type using the Loader. The TypeParser will
2287
+ # interpret the contained expression and the resolved type is remembered. This method also
2288
+ # checks and remembers if the resolve type contains self recursion.
2289
+ #
2290
+ # @param type_parser [TypeParser] type parser that will interpret the type expression
2291
+ # @param loader [Loader::Loader] loader to use when loading type aliases
2292
+ # @return [PTypeAliasType] the receiver of the call, i.e. `self`
2293
+ # @api private
2294
+ def resolve(type_parser, loader)
2295
+ if @resolved_type.nil?
2296
+ # resolved to PTypeReferenceType::DEFAULT during resolve to avoid endless recursion
2297
+ @resolved_type = PTypeReferenceType::DEFAULT
2298
+ @self_recursion = true # assumed while it being found out below
2299
+ begin
2300
+ @resolved_type = type_parser.interpret(@type_expr, loader).normalize
2301
+
2302
+ # Find out if this type is recursive. A recursive type has performance implications
2303
+ # on several methods and this knowledge is used to avoid that for non-recursive
2304
+ # types.
2305
+ guard = RecursionGuard.new
2306
+ accept(NoopTypeAcceptor::INSTANCE, guard)
2307
+ @self_recursion = guard.recursive_this?(self)
2308
+ rescue
2309
+ @resolved_type = nil
2310
+ raise
1544
2311
  end
1545
2312
  end
2313
+ self
2314
+ end
2315
+
2316
+ def eql?(o)
2317
+ super && o.name == @name
1546
2318
  end
1547
- end
1548
2319
 
2320
+ def accept(visitor, guard)
2321
+ guarded_recursion(guard, nil) do |g|
2322
+ super
2323
+ resolved_type.accept(visitor, g)
2324
+ end
2325
+ end
2326
+
2327
+ def self_recursion?
2328
+ @self_recursion
2329
+ end
2330
+
2331
+ protected
2332
+
2333
+ def _assignable?(o, guard)
2334
+ guard ||= RecursionGuard.new
2335
+ if guard.add_this(self) == RecursionGuard::SELF_RECURSION_IN_BOTH
2336
+ # Recursion detected both in self and other. This means that other is assignable
2337
+ # to self. This point would not have been reached otherwise
2338
+ true
2339
+ else
2340
+ resolved_type.assignable?(o, guard)
2341
+ end
2342
+ end
2343
+
2344
+ private
2345
+
2346
+ def guarded_recursion(guard, dflt)
2347
+ if @self_recursion
2348
+ guard ||= RecursionGuard.new
2349
+ (guard.add_this(self) & RecursionGuard::SELF_RECURSION_IN_THIS) == 0 ? yield(guard) : dflt
2350
+ else
2351
+ yield(guard)
2352
+ end
2353
+ end
2354
+
2355
+ DEFAULT = PTypeAliasType.new('UnresolvedAlias', nil, PTypeReferenceType::DEFAULT)
2356
+ end
2357
+ end
2358
+ end