inform-runtime 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +623 -0
  3. data/README.md +185 -0
  4. data/Rakefile +65 -0
  5. data/config/database.yml +37 -0
  6. data/exe/inform.rb +6 -0
  7. data/game/config.yml +5 -0
  8. data/game/example.inf +76 -0
  9. data/game/example.rb +90 -0
  10. data/game/forms/example_form.rb +2 -0
  11. data/game/grammar/game_grammar.inf.rb +11 -0
  12. data/game/languages/english.rb +2 -0
  13. data/game/models/example_model.rb +2 -0
  14. data/game/modules/example_module.rb +9 -0
  15. data/game/rules/example_state.rb +2 -0
  16. data/game/scripts/example_script.rb +2 -0
  17. data/game/topics/example_topic.rb +2 -0
  18. data/game/verbs/game_verbs.rb +15 -0
  19. data/game/verbs/metaverbs.rb +2028 -0
  20. data/lib/runtime/articles.rb +138 -0
  21. data/lib/runtime/builtins.rb +359 -0
  22. data/lib/runtime/color.rb +145 -0
  23. data/lib/runtime/command.rb +470 -0
  24. data/lib/runtime/config.rb +48 -0
  25. data/lib/runtime/context.rb +78 -0
  26. data/lib/runtime/daemon.rb +266 -0
  27. data/lib/runtime/database.rb +500 -0
  28. data/lib/runtime/events.rb +771 -0
  29. data/lib/runtime/experimental/handler_dsl.rb +175 -0
  30. data/lib/runtime/game.rb +74 -0
  31. data/lib/runtime/game_loader.rb +132 -0
  32. data/lib/runtime/grammar_parser.rb +553 -0
  33. data/lib/runtime/helpers.rb +177 -0
  34. data/lib/runtime/history.rb +45 -0
  35. data/lib/runtime/inflector.rb +195 -0
  36. data/lib/runtime/io.rb +174 -0
  37. data/lib/runtime/kernel.rb +450 -0
  38. data/lib/runtime/library.rb +59 -0
  39. data/lib/runtime/library_loader.rb +135 -0
  40. data/lib/runtime/link.rb +158 -0
  41. data/lib/runtime/logging.rb +197 -0
  42. data/lib/runtime/mixins.rb +570 -0
  43. data/lib/runtime/module.rb +202 -0
  44. data/lib/runtime/object.rb +761 -0
  45. data/lib/runtime/options.rb +104 -0
  46. data/lib/runtime/persistence.rb +292 -0
  47. data/lib/runtime/plurals.rb +60 -0
  48. data/lib/runtime/prototype.rb +307 -0
  49. data/lib/runtime/publication.rb +92 -0
  50. data/lib/runtime/runtime.rb +321 -0
  51. data/lib/runtime/session.rb +202 -0
  52. data/lib/runtime/stdlib.rb +604 -0
  53. data/lib/runtime/subscription.rb +47 -0
  54. data/lib/runtime/tag.rb +287 -0
  55. data/lib/runtime/tree.rb +204 -0
  56. data/lib/runtime/version.rb +24 -0
  57. data/lib/runtime/world_tree.rb +69 -0
  58. data/lib/runtime.rb +35 -0
  59. metadata +199 -0
@@ -0,0 +1,450 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # Copyright Nels Nelson 2008-2023 but freely usable (see license)
5
+ #
6
+ # This file is part of the Inform Runtime.
7
+ #
8
+ # The Inform Runtime is free software: you can redistribute it and/or
9
+ # modify it under the terms of the GNU General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # The Inform Runtime is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with the Inform Runtime. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ # Define a class inheriting from StandardError to raise when
22
+ # a method is already defined
23
+ class MethodAlreadyDefined < StandardError
24
+ ERROR_MESSAGE = 'Method %<method_name>s already defined'.freeze
25
+ def initialize(method_name)
26
+ super(format(ERROR_MESSAGE, method_name: method_name))
27
+ end
28
+ end
29
+
30
+ # The Kernel module
31
+ module Kernel
32
+ def import_global(symbol)
33
+ Inform::Runtime.import_global(symbol)
34
+ end
35
+
36
+ def Link(link)
37
+ Inform::Runtime.Link(link)
38
+ end
39
+
40
+ def Include(inclusion)
41
+ Inform::Runtime.instance.main_object = self if inclusion == "Parser"
42
+ Inform::Runtime.Include(inclusion)
43
+ end
44
+
45
+ def Attribute(attribute)
46
+ Inform::Runtime.Attribute(attribute)
47
+ end
48
+
49
+ def Property(property_or_modifier, modifier = nil, value = nil)
50
+ Inform::Runtime.Property(property_or_modifier, modifier, value)
51
+ end
52
+
53
+ def Default(property, value)
54
+ Inform::Runtime.Default(property, value)
55
+ end
56
+
57
+ def Constant(property, value)
58
+ Inform::Runtime.Constant(property, value)
59
+ end
60
+
61
+ # TODO: Remove
62
+ # def with(context = nil, &block)
63
+ # if context.nil?
64
+ # self.instance_eval(&block)
65
+ # else
66
+ # self.instance_exec(context, &block)
67
+ # end
68
+ # self.save if self.respond_to?(:save)
69
+ # self
70
+ # end
71
+
72
+ def return_after(*args, &block)
73
+ block.call
74
+ args.first
75
+ end
76
+
77
+ raise MethodAlreadyDefined, :reset_constant if respond_to?(:reset_constant)
78
+ def reset_constant(constant, value, target = Object)
79
+ return if constant.nil?
80
+ silence_warnings do
81
+ target.instance_eval { const_set(constant.to_sym, value) }
82
+ end
83
+ rescue StandardError => e
84
+ log.error "Unexpected error resetting constant #{constant}", e
85
+ nil
86
+ end
87
+
88
+ raise MethodAlreadyDefined, :toggle_constant if respond_to?(:toggle_constant)
89
+ def toggle_constant(constant, target = ::Object)
90
+ silence_warnings do
91
+ if target.const_defined?(constant)
92
+ target.instance_eval { remove_const(constant) }
93
+ else
94
+ target.instance_eval { const_set(constant, true) }
95
+ end
96
+ end
97
+ rescue StandardError => e
98
+ log.error "Unexpected error toggling constant #{constant}", e
99
+ nil
100
+ end
101
+
102
+ raise MethodAlreadyDefined, :get_constant if respond_to?(:get_constant)
103
+ # rubocop: disable Metrics/MethodLength
104
+ def get_constant(constant_name, target = Object)
105
+ constant = nil
106
+ begin
107
+ constant = target.const_get(constant_name)
108
+ rescue NameError => e
109
+ log.error "Undefined constant #{constant_name}: #{e}", e
110
+ rescue SyntaxError => e
111
+ log.error "Unexpected syntax error getting constant #{constant_name}", e
112
+ rescue StandardError => e
113
+ log.error "Unexpected error getting constant #{constant_name}", e
114
+ end
115
+ constant
116
+ end
117
+ # rubocop: enable Metrics/MethodLength
118
+
119
+ Cache = Struct.new(:classes, :modules)
120
+ Index = Cache.new({}, {})
121
+ NamespaceDelimiterPattern = %r{::}.freeze
122
+
123
+ raise MethodAlreadyDefined, :find_module if respond_to?(:find_module)
124
+ def find_module(module_name)
125
+ module_name = "::#{module_name}" unless NamespaceDelimiterPattern.match?(module_name)
126
+ mod = Index.modules[module_name]
127
+ # TODO: Test
128
+ # return mod if mod.is_a?(Module)
129
+
130
+ mod ||= get_constant(module_name)
131
+ return nil if mod.is_a?(Class)
132
+ Index.modules[module_name] = mod
133
+ end
134
+
135
+ raise MethodAlreadyDefined, :find_class if respond_to?(:find_class)
136
+ def find_class(class_name)
137
+ klass = Index.classes[class_name]
138
+ klass ||= get_constant(class_name)
139
+ Index.classes[class_name] = klass
140
+ end
141
+
142
+ raise MethodAlreadyDefined, :attempt_twice if respond_to?(:attempt_twice)
143
+ # rubocop: disable Metrics/MethodLength
144
+ def attempt_twice(&_block)
145
+ tries ||= 2
146
+ return yield
147
+ rescue StandardError => e
148
+ tries -= 1
149
+ if tries > 0
150
+ log.warn e.message
151
+ retry
152
+ else
153
+ log.error e
154
+ return nil
155
+ end
156
+ end
157
+ # rubocop: enable Metrics/MethodLength
158
+
159
+ raise MethodAlreadyDefined, :indent if respond_to?(:indent)
160
+ def indent(stack_depth = [0, caller(2).length - 9].max)
161
+ ' ' * stack_depth
162
+ end
163
+
164
+ raise MethodAlreadyDefined, :reset_runtime if respond_to?(:reset_runtime)
165
+ # rubocop: disable Metrics/AbcSize
166
+ def reset_runtime
167
+ org.jruby.Ruby.new_instance().use_as_global_runtime()
168
+ engine = javax.script.ScriptEngineManager.new().get_engine_by_name('jruby')
169
+ # rubocop: disable Style/RedundantInterpolation
170
+ log.warn "#{(engine.methods - Object.instance_methods).uniq.sort.inspect}"
171
+ # rubocop: enable Style/RedundantInterpolation
172
+ end
173
+ # rubocop: enable Metrics/AbcSize
174
+
175
+ raise MethodAlreadyDefined, :reload_feature if respond_to?(:reload_feature)
176
+ def reload_feature(feature)
177
+ silence_warnings do
178
+ # Remove a loaded feature:
179
+ load_service.remove_internal_loaded_feature(feature)
180
+ # Supposedly, purge old compiled script code:
181
+ reset_runtime
182
+ # Reload the feature:
183
+ load_service.load(feature, true)
184
+ end
185
+ end
186
+
187
+ raise MethodAlreadyDefined, :class_loader if respond_to?(:class_loader)
188
+ def class_loader
189
+ JRuby.runtime.jruby_class_loader
190
+ end
191
+
192
+ raise MethodAlreadyDefined, :load_service if respond_to?(:load_service)
193
+ def load_service
194
+ JRuby.runtime.load_service
195
+ end
196
+
197
+ raise MethodAlreadyDefined, :object_space if respond_to?(:object_space)
198
+ def object_space
199
+ JRuby.runtime.object_space
200
+ end
201
+
202
+ JavaPattern = %r{java}.freeze
203
+
204
+ def java?
205
+ JavaPattern.match?(RUBY_PLATFORM)
206
+ end
207
+
208
+ raise MethodAlreadyDefined, :memory_usage if respond_to?(:memory_usage)
209
+ def memory_usage
210
+ total_memory = java.lang.Runtime.getRuntime().totalMemory()
211
+ free_memory = java.lang.Runtime.getRuntime().freeMemory()
212
+ total_memory - free_memory
213
+ end
214
+
215
+ raise MethodAlreadyDefined, :collect_garbage if respond_to?(:collect_garbage)
216
+ # rubocop: disable Metrics/AbcSize
217
+ def collect_garbage
218
+ java.lang.System.gc()
219
+ sleep 0.1
220
+ java.lang.System.runFinalization()
221
+ sleep 0.1
222
+ rescue java.lang.Throwable => e
223
+ log.error "Unexpected java error invoking garbage collection: #{e}", e
224
+ rescue StandardError => e
225
+ log.error "Unexpected error invoking garbage collection: #{e}", e
226
+ end
227
+ # rubocop: enable Metrics/AbcSize
228
+
229
+ # Additional Inform-specific standard library routines
230
+ # TODO: externalize in another module
231
+ # The only reason these are included here is to ensure system-wide
232
+ # availability. If ubiquitous availability can be assured upon
233
+ # relocation, then proceed.
234
+ #
235
+ # TODO: Why on earth doesn't Inform::Object instances seem to
236
+ # inherit the built-in methods defined in the Inform module?
237
+ #
238
+ # module Inform
239
+ # class Object
240
+ # include Inform
241
+ #
242
+ # So when Spell < Inform::Object, for instance, why isn't the
243
+ # method Inform::light_adjusted(...) available?
244
+
245
+ raise MethodAlreadyDefined, :metaclass if respond_to?(:metaclass)
246
+ def metaclass(obj)
247
+ obj.class.metaclass
248
+ end
249
+
250
+ DefineNewRand = %(alias :original_rand :rand
251
+ def rand(ignore=0)
252
+ %<n>s
253
+ end
254
+ ).freeze
255
+ ResetOriginalRand = %(alias :rand :original_rand
256
+ remove_method :original_rand
257
+ ).freeze
258
+
259
+ raise MethodAlreadyDefined, :setrandom if respond_to?(:setrandom)
260
+ def setrandom(n)
261
+ puts "--------------------" # TODO: Remove
262
+ puts "Before setrandom Kernel.respond_to?(:original_rand) #{Kernel.respond_to?(:original_rand)}" # TODO: Remove
263
+ code = ResetOriginalRand if Kernel.respond_to?(:original_rand)
264
+ code ||= format(DefineNewRand, n: n)
265
+ log.debug "code:\n#{code}" # TODO: Remove
266
+ Kernel.module_eval(code)
267
+ ensure
268
+ puts "After setrandom Kernel.respond_to?(:original_rand) #{Kernel.respond_to?(:original_rand)}"
269
+ end
270
+
271
+ raise MethodAlreadyDefined, :label if respond_to?(:label)
272
+ def label(symbol, &_block)
273
+ loop do
274
+ catch symbol do
275
+ yield
276
+ break
277
+ end
278
+ end
279
+ end
280
+
281
+ raise MethodAlreadyDefined, :jump if respond_to?(:jump)
282
+ def jump(symbol)
283
+ throw symbol
284
+ end
285
+
286
+ raise MethodAlreadyDefined, :override if respond_to?(:override)
287
+ def override(replacement, method_name)
288
+ new_method = nil
289
+ begin
290
+ old_method = self.instance_method(method_name)
291
+ remove_method(method_name) if old_method.arity > 0
292
+ new_method = define_method(replacement.to_s, old_method)
293
+ rescue NameError => e
294
+ log.error "Error overriding method #{method_name}: #{e}", e
295
+ end
296
+ new_method
297
+ end
298
+
299
+ raise MethodAlreadyDefined, :silence_warnings if respond_to?(:silence_warnings)
300
+ def silence_warnings(&_block)
301
+ old_verbose = $VERBOSE
302
+ $VERBOSE = nil
303
+ yield
304
+ ensure
305
+ $VERBOSE = old_verbose
306
+ end
307
+
308
+ def child(obj = nil)
309
+ return nil if obj.nil?
310
+ return nil unless obj.respond_to?(:child)
311
+ obj.refresh if obj.respond_to?(:refresh)
312
+ obj.child.refresh
313
+ end
314
+
315
+ def parent_selector(obj, &block)
316
+ return nil if obj.nil?
317
+ loop do
318
+ obj = obj.parent
319
+ break if block.call(obj)
320
+ end
321
+ obj
322
+ end
323
+
324
+ # rubocop: disable Metrics/CyclomaticComplexity
325
+ # rubocop: disable Metrics/PerceivedComplexity
326
+ def parent(obj = nil, prop = :@parent, &block)
327
+ return nil if obj.nil?
328
+ obj.refresh if obj.respond_to?(:refresh)
329
+ obj_parent = nil
330
+ obj_parent = select_parent(obj, &block) if block_given?
331
+ obj_parent ||= obj.instance_variable_get(prop) if obj.instance_variable_defined?(prop)
332
+ obj_parent ||= obj.parent if obj.respond_to?(:parent)
333
+ return nil if obj_parent.nil?
334
+ obj_parent.refresh if obj_parent.respond_to?(:refresh)
335
+ obj_parent
336
+ end
337
+ # rubocop: enable Metrics/CyclomaticComplexity
338
+ # rubocop: enable Metrics/PerceivedComplexity
339
+
340
+ def sibling(obj = nil)
341
+ return nil if obj.nil?
342
+ return nil unless obj.respond_to?(:sibling)
343
+ obj.refresh if obj.respond_to?(:refresh)
344
+ obj.sibling.refresh
345
+ end
346
+
347
+ def children(obj = nil, prop = :@children)
348
+ return 0 if obj.nil?
349
+ obj.refresh if obj.respond_to?(:refresh)
350
+ if defined? db
351
+ return db.run('SELECT count(id) from "object" where parent_id = ' + obj.id) unless sysobj?
352
+ end
353
+ return obj.instance_variable_get(prop).length if obj.instance_variable_defined?(prop)
354
+ return unless obj.respond_to?(:children)
355
+ obj.children.length
356
+ end
357
+
358
+ def destroy(obj = nil)
359
+ return if obj.nil?
360
+ return unless obj.respond_to?(:has?)
361
+ return if obj.has? :prized
362
+ return unless obj.respond_to?(:destroy)
363
+ obj.destroy
364
+ end
365
+
366
+ def give(obj = nil, *attributes)
367
+ return if obj.nil?
368
+ return unless obj.respond_to?(:tag)
369
+ obj.tag(*attributes)
370
+ end
371
+
372
+ def take(obj = nil, *attributes)
373
+ return if obj.nil?
374
+ return unless obj.respond_to?(:untag)
375
+ obj.untag(*attributes)
376
+ end
377
+
378
+ # rubocop: disable Metrics/AbcSize
379
+ # rubocop: disable Metrics/CyclomaticComplexity
380
+ # rubocop: disable Metrics/PerceivedComplexity
381
+ def move(obj, new_parent)
382
+ return if obj.nil?
383
+ return unless new_parent.respond_to?(:<<)
384
+ return unless new_parent.respond_to?(:in?)
385
+ new_parent.safe_refresh if new_parent.respond_to?(:safe_refresh)
386
+ obj.safe_refresh if obj.respond_to?(:safe_refresh)
387
+ raise CycleError if new_parent.in? obj
388
+ obj.remove if obj.respond_to?(:remove)
389
+ new_parent << obj
390
+ obj.safe_refresh if obj.respond_to?(:safe_refresh)
391
+ new_parent.safe_refresh if new_parent.respond_to?(:safe_refresh)
392
+ end
393
+ # rubocop: enable Metrics/AbcSize
394
+ # rubocop: enable Metrics/CyclomaticComplexity
395
+ # rubocop: enable Metrics/PerceivedComplexity
396
+
397
+ def stand(obj)
398
+ take obj, :sitting
399
+ take obj, :kneeling
400
+ take obj, :prone
401
+ end
402
+
403
+ def sit(obj)
404
+ give obj, :sitting unless obj.has? :sitting
405
+ take obj, :kneeling
406
+ take obj, :prone
407
+ end
408
+
409
+ def kneel(obj)
410
+ give obj, :kneeling unless obj.has? :kneeling
411
+ take obj, :sitting
412
+ take obj, :prone
413
+ end
414
+
415
+ def recline(obj)
416
+ give obj, :prone unless obj.has? :prone
417
+ take obj, :sitting
418
+ take obj, :kneeling
419
+ end
420
+ end
421
+ # module Kernel
422
+
423
+ # The ObjectSpace module
424
+ module ObjectSpace
425
+ def self.enabled?
426
+ ObjectSpace.respond_to?(:each_object) && ObjectSpace.each_object {} # rubocop: disable Lint/EmptyBlock
427
+ true
428
+ rescue StandardError => e
429
+ log.debug "Error accessing object space: #{e.message}"
430
+ false
431
+ end
432
+ end
433
+
434
+ if defined?(Java)
435
+ JavaStackTracePattern = %r{(org[/.]+(java|jruby)|method_missing)}.freeze
436
+
437
+ # The Java module
438
+ module Java
439
+ # The Java::JavaLang module
440
+ module JavaLang
441
+ # The Java::JavaLang::Throwable class
442
+ class Throwable
443
+ def backtrace
444
+ self.getStackTrace().map(&:to_s).grep_v(JavaStackTracePattern)
445
+ end
446
+ end
447
+ end
448
+ end
449
+ end
450
+ # defined?(Java)
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # Copyright Nels Nelson 2008-2023 but freely usable (see license)
5
+ #
6
+ # This file is part of the Inform Runtime.
7
+ #
8
+ # The Inform Runtime is free software: you can redistribute it and/or
9
+ # modify it under the terms of the GNU General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # The Inform Runtime is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with the Inform Runtime. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ require_relative 'library_loader'
22
+
23
+ # The Inform module
24
+ module Inform
25
+ # The Library module
26
+ module Library
27
+ @methods_index = []
28
+
29
+ def methods_index
30
+ return @methods_index unless @methods_index.empty?
31
+ load_library_methods
32
+ @methods_index
33
+ end
34
+ module_function :methods_index
35
+
36
+ private
37
+
38
+ # rubocop: disable Metrics/AbcSize
39
+ # rubocop: disable Metrics/MethodLength
40
+ def load_library_methods
41
+ library_methods = Set.new
42
+ library_methods.merge(StdLib.instance_methods(false))
43
+ library_methods.merge(Inform::IO.instance_methods(false))
44
+ library_methods.merge(Inform.instance_methods(false))
45
+ library_methods.merge(InformLibrary.instance_methods(false))
46
+ Inform.included_modules.each do |mod|
47
+ library_methods.merge(mod.instance_methods(false))
48
+ end
49
+ InformLibrary.included_modules.each do |mod|
50
+ library_methods.merge(mod.instance_methods(false))
51
+ end
52
+ @methods_index.concat(library_methods.to_a).sort
53
+ log.debug "Loaded library methods: #{@methods_index}"
54
+ end
55
+ # rubocop: enable Metrics/AbcSize
56
+ # rubocop: enable Metrics/MethodLength
57
+ module_function :load_library_methods
58
+ end
59
+ end
@@ -0,0 +1,135 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # Copyright Nels Nelson 2008-2023 but freely usable (see license)
5
+ #
6
+ # This file is part of the Inform Runtime.
7
+ #
8
+ # The Inform Runtime is free software: you can redistribute it and/or
9
+ # modify it under the terms of the GNU General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # The Inform Runtime is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with the Inform Runtime. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ require 'set'
22
+
23
+ # The Inform module
24
+ module Inform
25
+ # The Inform::Library module
26
+ module Library
27
+ GEM_NAME = 'inform6lib'.freeze
28
+ GEM_PATH = Gem.loaded_specs[GEM_NAME]&.full_gem_path or raise "The #{GEM_NAME} gem is required"
29
+
30
+ DeclaredProperties = Struct.new(:memo).new({})
31
+ DeclaredAttributes = Struct.new(:memo).new(Set.new)
32
+ DeclaredGlobals = Struct.new(:memo).new([])
33
+ class LibraryLoadError < StandardError; end
34
+
35
+ def self.inform_gem_lib_path
36
+ @inform_gem_lib_path ||= File.expand_path(File.join(Inform::Library::GEM_PATH, 'lib'))
37
+ end
38
+
39
+ # The Library::Loader module provides methods for loading Library files.
40
+ module Loader
41
+ def load_library
42
+ overrides
43
+ end
44
+
45
+ def overrides
46
+ InformLibrary.extend(Inform::RuntimeLibrary)
47
+ manage_inform_library
48
+ end
49
+ end
50
+
51
+ # The Inform::Library::ClassMethods module
52
+ module ClassMethods
53
+ def Link(link)
54
+ # TODO: Implement
55
+ # Not a priority, because in the original Inform6 links were
56
+ # effectively just pointers to objects loaded in memory, and
57
+ # weren't persisted using a database relation.
58
+ end
59
+
60
+ InclusionGrammarGuessTemplate = '%<inclusion>s.h.inf.rb'.freeze
61
+ InclusionGuessesTemplate = '%<inclusion>s.h'.freeze
62
+
63
+ def Include(inclusion)
64
+ inclusion = Inform::Runtime.language_name if /^language__$/.match(inclusion)
65
+ grammar_guess = format(InclusionGrammarGuessTemplate, inclusion: inclusion)
66
+ guess = File.expand_path(
67
+ File.join(
68
+ Inform::Library.inform_gem_lib_path,
69
+ Inform::Runtime.inform_dir_path,
70
+ grammar_guess))
71
+ return Inform.load_grammar_by_path(guess) if File.exist?(guess)
72
+ require_first_existing(format(InclusionGuessesTemplate, inclusion: inclusion).split)
73
+ end
74
+
75
+ # rubocop: disable Metrics/AbcSize
76
+ # rubocop: disable Metrics/MethodLength
77
+ def require_first_existing(guesses)
78
+ guessed_file_path = File.join(Inform::Runtime.inform_dir_path, guesses.shift)
79
+ guessed_local_file_path = File.expand_path(guessed_file_path)
80
+ if File.exist?(guessed_local_file_path)
81
+ log.debug "Loading local library file: #{guessed_local_file_path}"
82
+ require_relative guessed_local_file_path
83
+ else
84
+ log.debug "Loading library: #{guessed_local_file_path}"
85
+ require guessed_file_path
86
+ end
87
+ rescue LoadError, StandardError => e
88
+ log.warn e.message
89
+ e.backtrace.each { |t| log.warn t } if defined?(Logging)
90
+ retry unless guesses.empty?
91
+ end
92
+ # rubocop: enable Metrics/AbcSize
93
+ # rubocop: enable Metrics/MethodLength
94
+
95
+ def Attribute(attribute)
96
+ DeclaredAttributes.memo << attribute
97
+ end
98
+
99
+ def declare_property(property, value, _modifier = nil)
100
+ DeclaredProperties.memo[property] ||= []
101
+ DeclaredProperties.memo[property] << value
102
+ end
103
+
104
+ PropertyModifiers = %i[additive].freeze
105
+ def Property(*args)
106
+ if PropertyModifiers.include?(modifier_or_property = args.shift)
107
+ declare_property(args.shift, args.shift, modifier_or_property)
108
+ else
109
+ declare_property(modifier_or_property, args.shift)
110
+ end
111
+ end
112
+
113
+ def Default(property, value)
114
+ Constant(property, value)
115
+ end
116
+
117
+ def Constant(constant, value, target = ::Object)
118
+ return if constant.nil?
119
+ silence_warnings do
120
+ target.instance_eval { const_set(constant.to_sym, value) }
121
+ end
122
+ rescue StandardError => e
123
+ log.error "Unexpected error resetting constant #{constant}", e
124
+ nil
125
+ end
126
+
127
+ def import_global(symbol)
128
+ DeclaredGlobals.memo << symbol
129
+ end
130
+ end
131
+ # module ClassMethods
132
+ end
133
+ # module Library
134
+ end
135
+ # module Inform