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.
- checksums.yaml +7 -0
- data/LICENSE +623 -0
- data/README.md +185 -0
- data/Rakefile +65 -0
- data/config/database.yml +37 -0
- data/exe/inform.rb +6 -0
- data/game/config.yml +5 -0
- data/game/example.inf +76 -0
- data/game/example.rb +90 -0
- data/game/forms/example_form.rb +2 -0
- data/game/grammar/game_grammar.inf.rb +11 -0
- data/game/languages/english.rb +2 -0
- data/game/models/example_model.rb +2 -0
- data/game/modules/example_module.rb +9 -0
- data/game/rules/example_state.rb +2 -0
- data/game/scripts/example_script.rb +2 -0
- data/game/topics/example_topic.rb +2 -0
- data/game/verbs/game_verbs.rb +15 -0
- data/game/verbs/metaverbs.rb +2028 -0
- data/lib/runtime/articles.rb +138 -0
- data/lib/runtime/builtins.rb +359 -0
- data/lib/runtime/color.rb +145 -0
- data/lib/runtime/command.rb +470 -0
- data/lib/runtime/config.rb +48 -0
- data/lib/runtime/context.rb +78 -0
- data/lib/runtime/daemon.rb +266 -0
- data/lib/runtime/database.rb +500 -0
- data/lib/runtime/events.rb +771 -0
- data/lib/runtime/experimental/handler_dsl.rb +175 -0
- data/lib/runtime/game.rb +74 -0
- data/lib/runtime/game_loader.rb +132 -0
- data/lib/runtime/grammar_parser.rb +553 -0
- data/lib/runtime/helpers.rb +177 -0
- data/lib/runtime/history.rb +45 -0
- data/lib/runtime/inflector.rb +195 -0
- data/lib/runtime/io.rb +174 -0
- data/lib/runtime/kernel.rb +450 -0
- data/lib/runtime/library.rb +59 -0
- data/lib/runtime/library_loader.rb +135 -0
- data/lib/runtime/link.rb +158 -0
- data/lib/runtime/logging.rb +197 -0
- data/lib/runtime/mixins.rb +570 -0
- data/lib/runtime/module.rb +202 -0
- data/lib/runtime/object.rb +761 -0
- data/lib/runtime/options.rb +104 -0
- data/lib/runtime/persistence.rb +292 -0
- data/lib/runtime/plurals.rb +60 -0
- data/lib/runtime/prototype.rb +307 -0
- data/lib/runtime/publication.rb +92 -0
- data/lib/runtime/runtime.rb +321 -0
- data/lib/runtime/session.rb +202 -0
- data/lib/runtime/stdlib.rb +604 -0
- data/lib/runtime/subscription.rb +47 -0
- data/lib/runtime/tag.rb +287 -0
- data/lib/runtime/tree.rb +204 -0
- data/lib/runtime/version.rb +24 -0
- data/lib/runtime/world_tree.rb +69 -0
- data/lib/runtime.rb +35 -0
- 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
|