fable 0.5.0
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/.circleci/config.yml +30 -0
- data/.gitignore +57 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +21 -0
- data/README.md +2 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/test +8 -0
- data/fable.gemspec +34 -0
- data/fable.sublime-project +8 -0
- data/lib/fable.rb +49 -0
- data/lib/fable/call_stack.rb +351 -0
- data/lib/fable/choice.rb +31 -0
- data/lib/fable/choice_point.rb +65 -0
- data/lib/fable/container.rb +218 -0
- data/lib/fable/control_command.rb +156 -0
- data/lib/fable/debug_metadata.rb +13 -0
- data/lib/fable/divert.rb +100 -0
- data/lib/fable/glue.rb +7 -0
- data/lib/fable/ink_list.rb +425 -0
- data/lib/fable/list_definition.rb +44 -0
- data/lib/fable/list_definitions_origin.rb +35 -0
- data/lib/fable/native_function_call.rb +324 -0
- data/lib/fable/native_function_operations.rb +149 -0
- data/lib/fable/observer.rb +205 -0
- data/lib/fable/path.rb +186 -0
- data/lib/fable/pointer.rb +42 -0
- data/lib/fable/profiler.rb +287 -0
- data/lib/fable/push_pop_type.rb +11 -0
- data/lib/fable/runtime_object.rb +159 -0
- data/lib/fable/search_result.rb +20 -0
- data/lib/fable/serializer.rb +560 -0
- data/lib/fable/state_patch.rb +47 -0
- data/lib/fable/story.rb +1447 -0
- data/lib/fable/story_state.rb +915 -0
- data/lib/fable/tag.rb +14 -0
- data/lib/fable/value.rb +334 -0
- data/lib/fable/variable_assignment.rb +20 -0
- data/lib/fable/variable_reference.rb +38 -0
- data/lib/fable/variables_state.rb +327 -0
- data/lib/fable/version.rb +3 -0
- data/lib/fable/void.rb +4 -0
- data/zork_mode.rb +23 -0
- metadata +149 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
module Fable
|
2
|
+
class RuntimeObject
|
3
|
+
# RuntimeObjects can be included in the main story as a hierarchy
|
4
|
+
# usually parents are container objectsd
|
5
|
+
attr_accessor :parent, :own_debug_metadata, :path, :original_object
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@path = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def debug_metadata
|
12
|
+
if @own_debug_metadata.nil?
|
13
|
+
if !parent.nil?
|
14
|
+
return parent.debug_metadata || DebugMetadata.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
return @own_debug_metadata
|
19
|
+
end
|
20
|
+
|
21
|
+
def indentation_string(indentation = 0)
|
22
|
+
" " * indentation
|
23
|
+
end
|
24
|
+
|
25
|
+
def debug_line_number_of_path(path)
|
26
|
+
return nil if path.nil?
|
27
|
+
|
28
|
+
# Try to get a line number from debug metadata
|
29
|
+
root = self.root_content_container
|
30
|
+
|
31
|
+
if !root.nil?
|
32
|
+
target_content = root.content_at_path(path).object
|
33
|
+
if !target_content.nil?
|
34
|
+
target_debug_metadata = target_content.debug_metadata
|
35
|
+
if !target_debug_metadata.nil?
|
36
|
+
target_debug_metadata.start_line_number
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def path
|
43
|
+
if !defined?(@path) || @path.nil?
|
44
|
+
if parent.nil?
|
45
|
+
@path = Path.new("")
|
46
|
+
else
|
47
|
+
# Maintain a stack so that the order of the components is reversed
|
48
|
+
# when they're added to the Path. We're iterating up from the
|
49
|
+
# leaves/children to the root
|
50
|
+
components = []
|
51
|
+
|
52
|
+
child = self
|
53
|
+
container = child.parent
|
54
|
+
|
55
|
+
while !container.nil?
|
56
|
+
if child.is_a?(Container) && child.valid_name?
|
57
|
+
components << Path::Component.new(name: child.name)
|
58
|
+
else
|
59
|
+
components << Path::Component.new(index: container.content.index(child))
|
60
|
+
end
|
61
|
+
|
62
|
+
child = container
|
63
|
+
container = container.parent
|
64
|
+
end
|
65
|
+
|
66
|
+
@path = Path.new(components.reverse)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
return @path
|
71
|
+
end
|
72
|
+
|
73
|
+
def resolve_path(path)
|
74
|
+
if path.relative?
|
75
|
+
nearest_container = self
|
76
|
+
|
77
|
+
if !nearest_container.is_a?(Container)
|
78
|
+
nearest_container = self.parent
|
79
|
+
path = path.tail
|
80
|
+
end
|
81
|
+
|
82
|
+
return nearest_container.content_at_path(path)
|
83
|
+
else
|
84
|
+
return self.root_content_container.content_at_path(path)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def convert_path_to_relative(global_path)
|
89
|
+
# 1. Find last shared ancestor
|
90
|
+
# 2. Drill up using '..' style (actually represented as "^")
|
91
|
+
# 3. Re-build downward chain from common ancestor
|
92
|
+
|
93
|
+
own_path = self.path
|
94
|
+
|
95
|
+
min_path_length = [global_path.length, own_path.length].min
|
96
|
+
last_shared_path_comp_index = -1
|
97
|
+
(0..min_path_length).each do |i|
|
98
|
+
own_component = own_path.components[i]
|
99
|
+
other_component = global_path.components[i]
|
100
|
+
|
101
|
+
if own_component == other_component
|
102
|
+
last_shared_path_comp_index = i
|
103
|
+
else
|
104
|
+
break
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# No shared path components, so just use global path
|
109
|
+
if last_shared_path_comp_index == -1
|
110
|
+
return global_path
|
111
|
+
end
|
112
|
+
|
113
|
+
number_of_upwards_moves = (own_path.length - 1) - last_shared_path_comp_index - 1
|
114
|
+
new_path_components = []
|
115
|
+
|
116
|
+
(0..number_of_upwards_moves).each do |i|
|
117
|
+
new_path_components << Path::Component.parent_component
|
118
|
+
end
|
119
|
+
|
120
|
+
(last_shared_path_comp_index + 1..global_path.length).each do |i|
|
121
|
+
new_path_components << global_path.components[i]
|
122
|
+
end
|
123
|
+
|
124
|
+
return Path.new(new_path_components, true)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Find the most compact representation for a path,
|
128
|
+
# whether relative or global
|
129
|
+
def compact_path_string(other_path)
|
130
|
+
if other_path.relative?
|
131
|
+
relative_path_string = other_path.components_string
|
132
|
+
global_path_string = self.path.path_by_appending_path(other_path).components_string
|
133
|
+
else
|
134
|
+
relative_path = convert_path_to_relative(other_path)
|
135
|
+
relative_path_string = relative_path.components_string
|
136
|
+
global_path_string = other_path.components_string
|
137
|
+
end
|
138
|
+
|
139
|
+
if relative_path_string.length < global_path_string.length
|
140
|
+
return relative_path_string
|
141
|
+
else
|
142
|
+
return global_path_string
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def root_content_container
|
147
|
+
ancestor = self
|
148
|
+
while !ancestor.parent.nil?
|
149
|
+
ancestor = ancestor.parent
|
150
|
+
end
|
151
|
+
|
152
|
+
return ancestor
|
153
|
+
end
|
154
|
+
|
155
|
+
def copy
|
156
|
+
raise NotImplementedError, "#{self.class} doesn't support copying"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Fable
|
2
|
+
class SearchResult
|
3
|
+
attr_accessor :object, :approximate
|
4
|
+
|
5
|
+
alias_method :approximate?, :approximate
|
6
|
+
|
7
|
+
def correct_object
|
8
|
+
if approximate?
|
9
|
+
return nil
|
10
|
+
else
|
11
|
+
return object
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def container
|
16
|
+
return nil if !object.is_a?(Container)
|
17
|
+
return object
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,560 @@
|
|
1
|
+
module Fable
|
2
|
+
class Serializer
|
3
|
+
def self.convert_to_runtime_objects(objects, skip_last = false)
|
4
|
+
last_item = objects.last
|
5
|
+
runtime_objects = []
|
6
|
+
|
7
|
+
objects.each do |object|
|
8
|
+
next if object == last_item && skip_last
|
9
|
+
runtime_objects << convert_to_runtime_object(object)
|
10
|
+
end
|
11
|
+
|
12
|
+
runtime_objects
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.convert_to_runtime_objects_hash(hash)
|
16
|
+
runtime_hash = {}
|
17
|
+
|
18
|
+
hash.each do |key, value|
|
19
|
+
runtime_hash[key] = convert_to_runtime_object(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
return runtime_hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.array_to_container(array)
|
26
|
+
contents = convert_to_runtime_objects(array, true)
|
27
|
+
# Final object in the array is always a combination of
|
28
|
+
# - named content
|
29
|
+
# - an #f key with the count flags
|
30
|
+
final_object = array.last
|
31
|
+
bit_flags = 0
|
32
|
+
container_name = nil
|
33
|
+
named_only_content = {}
|
34
|
+
|
35
|
+
if !final_object.nil?
|
36
|
+
final_object.each do |key, value|
|
37
|
+
if key == "#f"
|
38
|
+
bit_flags = value.to_i
|
39
|
+
elsif key == "#n"
|
40
|
+
container_name = value
|
41
|
+
else
|
42
|
+
named_content_item = convert_to_runtime_object(value)
|
43
|
+
if named_content_item.is_a?(Container)
|
44
|
+
named_content_item.name = key
|
45
|
+
end
|
46
|
+
|
47
|
+
named_only_content[key] = named_content_item
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
container = Container.new(bit_flags)
|
53
|
+
container.add_content(contents)
|
54
|
+
if !container_name.nil?
|
55
|
+
container.name = container_name
|
56
|
+
end
|
57
|
+
|
58
|
+
named_only_content.each do |key, content|
|
59
|
+
container.add_to_named_content(content)
|
60
|
+
end
|
61
|
+
|
62
|
+
return container
|
63
|
+
end
|
64
|
+
|
65
|
+
# ----------------------
|
66
|
+
# JSON ENCODING SCHEME
|
67
|
+
# ----------------------
|
68
|
+
#
|
69
|
+
# Glue: "<>", "G<", "G>"
|
70
|
+
#
|
71
|
+
# ControlCommand: "ev", "out", "/ev", "du" "pop", "->->", "~ret", "str", "/str", "nop",
|
72
|
+
# "choiceCnt", "turns", "visit", "seq", "thread", "done", "end"
|
73
|
+
#
|
74
|
+
# NativeFunction: "+", "-", "/", "*", "%" "~", "==", ">", "<", ">=", "<=", "!=", "!"... etc
|
75
|
+
#
|
76
|
+
# Void: "void"
|
77
|
+
#
|
78
|
+
# Value: "^string value", "^^string value beginning with ^"
|
79
|
+
# 5, 5.2
|
80
|
+
# {"^->": "path.target"}
|
81
|
+
# {"^var": "varname", "ci": 0}
|
82
|
+
#
|
83
|
+
# Container: [...]
|
84
|
+
# [...,
|
85
|
+
# {
|
86
|
+
# "subContainerName": ...,
|
87
|
+
# "#f": 5, # flags
|
88
|
+
# "#n": "containerOwnName" # only if not redundant
|
89
|
+
# }
|
90
|
+
# ]
|
91
|
+
#
|
92
|
+
# Divert: {"->": "path.target", "c": true }
|
93
|
+
# {"->": "path.target", "var": true}
|
94
|
+
# {"f()": "path.func"}
|
95
|
+
# {"->t->": "path.tunnel"}
|
96
|
+
# {"x()": "externalFuncName", "exArgs": 5}
|
97
|
+
#
|
98
|
+
# Var Assign: {"VAR=": "varName", "re": true} # reassignment
|
99
|
+
# {"temp=": "varName"}
|
100
|
+
#
|
101
|
+
# Var ref: {"VAR?": "varName"}
|
102
|
+
# {"CNT?": "stitch name"}
|
103
|
+
#
|
104
|
+
# ChoicePoint: {"*": pathString,
|
105
|
+
# "flg": 18 }
|
106
|
+
#
|
107
|
+
# Choice: Nothing too clever, it's only used in the save state,
|
108
|
+
# there's not likely to be many of them.
|
109
|
+
#
|
110
|
+
# Tag: {"#": "the tag text"}
|
111
|
+
|
112
|
+
def self.convert_to_runtime_object(object)
|
113
|
+
case object
|
114
|
+
when Numeric
|
115
|
+
return Value.create(object)
|
116
|
+
when String
|
117
|
+
string = object
|
118
|
+
|
119
|
+
# StringValue
|
120
|
+
if string.start_with?("^")
|
121
|
+
return StringValue.new(string[1..-1])
|
122
|
+
elsif string.start_with?("\n") && string.length == 1
|
123
|
+
return StringValue.new("\n")
|
124
|
+
end
|
125
|
+
|
126
|
+
# Glue
|
127
|
+
if string == "<>"
|
128
|
+
return Glue.new
|
129
|
+
end
|
130
|
+
|
131
|
+
# Control Commands
|
132
|
+
if ControlCommand.is_control_command?(string)
|
133
|
+
return ControlCommand.get_control_command(string)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Native functions
|
137
|
+
# "^" conflicts with the way we identify strings, so now
|
138
|
+
# we know it's not a string, we can convert back to the proper symbol
|
139
|
+
# for this operator
|
140
|
+
if string == "L^"
|
141
|
+
string = "^"
|
142
|
+
end
|
143
|
+
|
144
|
+
if NativeFunctionCall.is_native_function?(string)
|
145
|
+
return NativeFunctionCall.new(string)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Pop
|
149
|
+
if string == ControlCommand::COMMANDS[:POP_TUNNEL]
|
150
|
+
return ControlCommand.get_control_command(string)
|
151
|
+
elsif string == ControlCommand::COMMANDS[:POP_FUNCTION]
|
152
|
+
return ControlCommand.get_control_command(string)
|
153
|
+
end
|
154
|
+
|
155
|
+
if string == "void"
|
156
|
+
return Void.new
|
157
|
+
end
|
158
|
+
when Hash
|
159
|
+
given_hash = object
|
160
|
+
|
161
|
+
# Divert target value to path
|
162
|
+
if given_hash["^->"]
|
163
|
+
return DivertTargetValue.new(Path.new(given_hash["^->"]))
|
164
|
+
end
|
165
|
+
|
166
|
+
# VariablePointerType
|
167
|
+
if given_hash["^var"]
|
168
|
+
variable_pointer = VariablePointerValue.new(given_hash["^var"])
|
169
|
+
if given_hash["ci"]
|
170
|
+
variable_pointer.context_index = given_hash["ci"]
|
171
|
+
end
|
172
|
+
|
173
|
+
return variable_pointer
|
174
|
+
end
|
175
|
+
|
176
|
+
# Divert
|
177
|
+
is_divert = false
|
178
|
+
pushes_to_stack = false
|
179
|
+
divert_push_pop_type = PushPopType::TYPES[:function]
|
180
|
+
external = false
|
181
|
+
value = nil
|
182
|
+
|
183
|
+
if given_hash["->"]
|
184
|
+
is_divert = true
|
185
|
+
value = given_hash["->"]
|
186
|
+
elsif given_hash["f()"]
|
187
|
+
is_divert = true
|
188
|
+
pushes_to_stack = true
|
189
|
+
divert_push_pop_type = PushPopType::TYPES[:function]
|
190
|
+
value = given_hash["f()"]
|
191
|
+
elsif given_hash["->t->"]
|
192
|
+
is_divert = true
|
193
|
+
pushes_to_stack = true
|
194
|
+
divert_push_pop_type = PushPopType::TYPES[:tunnel]
|
195
|
+
value = given_hash["->t->"]
|
196
|
+
elsif given_hash["x()"]
|
197
|
+
is_divert = true
|
198
|
+
external = true
|
199
|
+
pushes_to_stack = false
|
200
|
+
divert_push_pop_type = PushPopType::TYPES[:function]
|
201
|
+
value = given_hash["x()"]
|
202
|
+
end
|
203
|
+
|
204
|
+
if is_divert
|
205
|
+
divert = Divert.new
|
206
|
+
divert.pushes_to_stack = pushes_to_stack
|
207
|
+
divert.stack_push_type = divert_push_pop_type
|
208
|
+
divert.is_external = external
|
209
|
+
target = value.to_s
|
210
|
+
|
211
|
+
if given_hash["var"]
|
212
|
+
divert.variable_divert_name = target
|
213
|
+
else
|
214
|
+
divert.target_path_string = target
|
215
|
+
end
|
216
|
+
|
217
|
+
divert.is_conditional = given_hash.has_key?("c")
|
218
|
+
|
219
|
+
if external
|
220
|
+
if given_hash["exArgs"]
|
221
|
+
divert.external_arguments = given_hash["exArgs"]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
return divert
|
226
|
+
end
|
227
|
+
|
228
|
+
# Choice
|
229
|
+
if given_hash["*"]
|
230
|
+
choice = ChoicePoint.new
|
231
|
+
choice.path_string_on_choice = given_hash["*"]
|
232
|
+
|
233
|
+
if given_hash["flg"]
|
234
|
+
choice.flags = given_hash["flg"]
|
235
|
+
end
|
236
|
+
|
237
|
+
return choice
|
238
|
+
end
|
239
|
+
|
240
|
+
# Variable Reference
|
241
|
+
if given_hash["VAR?"]
|
242
|
+
return VariableReference.new(given_hash["VAR?"])
|
243
|
+
elsif given_hash["CNT?"]
|
244
|
+
read_count_variable_reference = VariableReference.new(nil)
|
245
|
+
read_count_variable_reference.path_string_for_count = given_hash["CNT?"]
|
246
|
+
return read_count_variable_reference
|
247
|
+
end
|
248
|
+
|
249
|
+
# Variable Assignment
|
250
|
+
is_variable_assignment = false
|
251
|
+
is_global_variable = false
|
252
|
+
|
253
|
+
if given_hash["VAR="]
|
254
|
+
variable_name = given_hash["VAR="]
|
255
|
+
is_variable_assignment = true
|
256
|
+
is_global_variable = true
|
257
|
+
elsif given_hash["temp="]
|
258
|
+
variable_name = given_hash["temp="]
|
259
|
+
is_variable_assignment = true
|
260
|
+
is_global_variable = false
|
261
|
+
end
|
262
|
+
|
263
|
+
if is_variable_assignment
|
264
|
+
is_new_declaration = !given_hash.has_key?("re")
|
265
|
+
variable_assignment = VariableAssignment.new(variable_name, is_new_declaration)
|
266
|
+
variable_assignment.global = is_global_variable
|
267
|
+
return variable_assignment
|
268
|
+
end
|
269
|
+
|
270
|
+
# Tag
|
271
|
+
if given_hash["#"]
|
272
|
+
return Tag.new(given_hash["#"])
|
273
|
+
end
|
274
|
+
|
275
|
+
# List
|
276
|
+
if given_hash["list"]
|
277
|
+
list_content = given_hash["list"]
|
278
|
+
raw_list = InkList.new
|
279
|
+
if given_hash["origins"]
|
280
|
+
raw_list.set_initial_origin_names(given_hash["origins"])
|
281
|
+
end
|
282
|
+
|
283
|
+
list_content.each do |key, value|
|
284
|
+
item = InkList::InkListItem.new(full_name: key)
|
285
|
+
raw_list.list[item] = value.to_i
|
286
|
+
end
|
287
|
+
|
288
|
+
return ListValue.new(raw_list)
|
289
|
+
end
|
290
|
+
|
291
|
+
# Used when serializing save state only
|
292
|
+
if given_hash["originalChoicePath"]
|
293
|
+
return object_to_choice(object)
|
294
|
+
end
|
295
|
+
when Array
|
296
|
+
return array_to_container(object)
|
297
|
+
when NilClass
|
298
|
+
return nil
|
299
|
+
end
|
300
|
+
|
301
|
+
raise Error, "Failed to convert to runtime object: #{object}"
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.object_to_choice(object)
|
305
|
+
choice = Choice.new
|
306
|
+
choice.text = object["text"]
|
307
|
+
choice.index = object["index"].to_i
|
308
|
+
choice.source_path = object["originalChoicePath"]
|
309
|
+
choice.original_thread_index = object["originalThreadIndex"].to_i
|
310
|
+
choice.path_string_on_choice = object["targetPath"]
|
311
|
+
return choice
|
312
|
+
end
|
313
|
+
|
314
|
+
def self.convert_to_list_definitions(object)
|
315
|
+
all_definitions = []
|
316
|
+
|
317
|
+
object.each do |name, list_definition_hash|
|
318
|
+
items = {}
|
319
|
+
list_definition_hash.each do |key, value|
|
320
|
+
items[key] = value.to_i
|
321
|
+
end
|
322
|
+
|
323
|
+
all_definitions << ListDefinition.new(name, items)
|
324
|
+
end
|
325
|
+
|
326
|
+
return ListDefinitionsOrigin.new(all_definitions)
|
327
|
+
end
|
328
|
+
|
329
|
+
def self.convert_hash_of_runtime_objects(hash_value)
|
330
|
+
result = {}
|
331
|
+
hash_value.each do |key, value|
|
332
|
+
result[key] = convert_from_runtime_object(value)
|
333
|
+
end
|
334
|
+
|
335
|
+
result
|
336
|
+
end
|
337
|
+
|
338
|
+
def self.convert_array_of_runtime_objects(array)
|
339
|
+
array.map{|x| convert_from_runtime_object(x) }
|
340
|
+
end
|
341
|
+
|
342
|
+
def self.convert_from_runtime_object(object)
|
343
|
+
if object.is_a?(Container)
|
344
|
+
return convert_from_container(object)
|
345
|
+
end
|
346
|
+
|
347
|
+
if object.is_a?(Divert)
|
348
|
+
return convert_from_divert(object)
|
349
|
+
end
|
350
|
+
|
351
|
+
if object.is_a?(ChoicePoint)
|
352
|
+
return convert_from_choice_point(object)
|
353
|
+
end
|
354
|
+
|
355
|
+
if object.is_a?(IntValue) || object.is_a?(FloatValue)
|
356
|
+
return object.value
|
357
|
+
end
|
358
|
+
|
359
|
+
if object.is_a?(StringValue)
|
360
|
+
return convert_from_string_value(object)
|
361
|
+
end
|
362
|
+
|
363
|
+
if object.is_a?(ListValue) || object.is_a?(InkList)
|
364
|
+
return convert_from_list(object)
|
365
|
+
end
|
366
|
+
|
367
|
+
if object.is_a?(DivertTargetValue)
|
368
|
+
return convert_divert_target_value(object)
|
369
|
+
end
|
370
|
+
|
371
|
+
if object.is_a?(VariablePointerValue)
|
372
|
+
return convert_variable_pointer_value(object)
|
373
|
+
end
|
374
|
+
|
375
|
+
if object.is_a?(Glue)
|
376
|
+
return convert_glue(object)
|
377
|
+
end
|
378
|
+
|
379
|
+
if object.is_a?(ControlCommand)
|
380
|
+
return convert_control_command(object)
|
381
|
+
end
|
382
|
+
|
383
|
+
if object.is_a?(NativeFunctionCall)
|
384
|
+
return convert_native_function_call(object)
|
385
|
+
end
|
386
|
+
|
387
|
+
if object.is_a?(VariableReference)
|
388
|
+
return convert_variable_reference(object)
|
389
|
+
end
|
390
|
+
|
391
|
+
if object.is_a?(VariableAssignment)
|
392
|
+
return convert_variable_assignment(object)
|
393
|
+
end
|
394
|
+
|
395
|
+
if object.is_a?(Void)
|
396
|
+
return convert_void
|
397
|
+
end
|
398
|
+
|
399
|
+
if object.is_a?(Tag)
|
400
|
+
return convert_tag(object)
|
401
|
+
end
|
402
|
+
|
403
|
+
if object.is_a?(Choice)
|
404
|
+
return convert_choice(object)
|
405
|
+
end
|
406
|
+
|
407
|
+
raise ArgumentError, "Failed to serialize runtime object: #{object}"
|
408
|
+
end
|
409
|
+
|
410
|
+
def self.convert_from_divert(object)
|
411
|
+
result = {}
|
412
|
+
divert_type_key = "->"
|
413
|
+
|
414
|
+
if divert.external?
|
415
|
+
divert_type_key = "x()"
|
416
|
+
elsif divert.pushes_to_stack?
|
417
|
+
if divert.stack_push_type == PushPopType::TYPES[:function]
|
418
|
+
divert_type_key = "f()"
|
419
|
+
else
|
420
|
+
divert_type_key = "->t->"
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
if divert.has_variable_target?
|
425
|
+
target_string = divert.variable_divert_name
|
426
|
+
else
|
427
|
+
target_string = divert.target_path_string
|
428
|
+
end
|
429
|
+
|
430
|
+
result[divert_type_key] = target_string
|
431
|
+
|
432
|
+
if divert.has_variable_target?
|
433
|
+
result["var"] = true
|
434
|
+
end
|
435
|
+
|
436
|
+
if divert.is_conditional?
|
437
|
+
result["c"] = true
|
438
|
+
end
|
439
|
+
|
440
|
+
if divert.external_arguments > 0
|
441
|
+
result["exArgs"] = divert.external_arguments
|
442
|
+
end
|
443
|
+
|
444
|
+
return result
|
445
|
+
end
|
446
|
+
|
447
|
+
def self.convert_from_choice_point(choice_point)
|
448
|
+
return {
|
449
|
+
"*" => choice_point.path_string_on_choice,
|
450
|
+
"flg" => choice_point.flags
|
451
|
+
}
|
452
|
+
end
|
453
|
+
|
454
|
+
def self.convert_from_string_value(string_value)
|
455
|
+
if string_value.is_newline?
|
456
|
+
return "\n"
|
457
|
+
else
|
458
|
+
return "^#{string_value.value}"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
def self.convert_from_list(list_value)
|
463
|
+
result = {}
|
464
|
+
if list_value.is_a?(ListValue)
|
465
|
+
raw_list = list_value.value
|
466
|
+
else
|
467
|
+
raw_list = list_value
|
468
|
+
end
|
469
|
+
|
470
|
+
raw_list.list.each do |list_item, value|
|
471
|
+
result[list_item.full_name] = value
|
472
|
+
end
|
473
|
+
|
474
|
+
if raw_list.list.empty? && !raw_list.origin_names.nil? && !raw_list.origin_names.empty?
|
475
|
+
result["origins"] = raw_list.origin_names
|
476
|
+
end
|
477
|
+
|
478
|
+
return {"list" => result}
|
479
|
+
end
|
480
|
+
|
481
|
+
def self.convert_divert_target_value(divert_target_value)
|
482
|
+
return {"^->" => divert_target_value.value.components_string }
|
483
|
+
end
|
484
|
+
|
485
|
+
def self.convert_variable_pointer_value(variable_pointer_value)
|
486
|
+
return {
|
487
|
+
"^var" => variable_pointer_value.value,
|
488
|
+
"ci" => variable_pointer_value.context_index
|
489
|
+
}
|
490
|
+
end
|
491
|
+
|
492
|
+
def self.convert_glue(glue)
|
493
|
+
return "<>"
|
494
|
+
end
|
495
|
+
|
496
|
+
def self.convert_control_command(control_command)
|
497
|
+
return control_command.command_type
|
498
|
+
end
|
499
|
+
|
500
|
+
def self.convert_native_function_call(native_function_call)
|
501
|
+
function_name = native_function_call.name
|
502
|
+
if name == "^"
|
503
|
+
return "L^"
|
504
|
+
else
|
505
|
+
return name
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
def self.convert_variable_reference(variable_reference)
|
510
|
+
read_count_path = variable_reference.path_string_for_count
|
511
|
+
if read_count_path.nil?
|
512
|
+
return {"CNT?" => read_count_path}
|
513
|
+
else
|
514
|
+
return {"VAR?" => variable_reference.name}
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
def self.convert_variable_assignment(variable_assignment)
|
519
|
+
result = {}
|
520
|
+
|
521
|
+
if variable_assignment.global?
|
522
|
+
key = "VAR="
|
523
|
+
else
|
524
|
+
key = "temp="
|
525
|
+
end
|
526
|
+
|
527
|
+
result[key] = variable_assignment.variable_name
|
528
|
+
|
529
|
+
if !variable_assignment.new_declaration?
|
530
|
+
result["re"] = true
|
531
|
+
end
|
532
|
+
|
533
|
+
return result
|
534
|
+
end
|
535
|
+
|
536
|
+
def self.convert_void
|
537
|
+
return "void"
|
538
|
+
end
|
539
|
+
|
540
|
+
def self.convert_tag(tag)
|
541
|
+
return {
|
542
|
+
"#" => tag.text
|
543
|
+
}
|
544
|
+
end
|
545
|
+
|
546
|
+
def self.convert_choice(choice)
|
547
|
+
return {
|
548
|
+
"text" => choice.text,
|
549
|
+
"index" => choice.index,
|
550
|
+
"originalChoicePath" => choice.source_path,
|
551
|
+
"originalThreadIndex" => choice.original_thread_index,
|
552
|
+
"targetPath" => choice.path_string_on_choice
|
553
|
+
}
|
554
|
+
end
|
555
|
+
|
556
|
+
def self.convert_choices(choices)
|
557
|
+
choices.map{|choice| convert_choice(choice) }
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|