This commit is contained in:
Gerard Gascón 2025-04-24 17:23:34 +02:00
commit b99855351d
434 changed files with 50357 additions and 0 deletions

View file

@ -0,0 +1,200 @@
# warning-ignore-all:unused_class_variable
# warning-ignore-all:shadowed_variable
# ############################################################################ #
# Copyright © 2015-2021 inkle Ltd.
# Copyright © 2019-2022 Frédéric Maquin <fred@ephread.com>
# All Rights Reserved
#
# This file is part of inkgd.
# inkgd is licensed under the terms of the MIT license.
# ############################################################################ #
extends Node
# Hiding this type to prevent registration of "private" nodes.
# See https://github.com/godotengine/godot-proposals/issues/1047
# class_name InkRuntimeNode
# Expected to be added to the SceneTree as a singleton object.
# ############################################################################ #
# Imports
# ############################################################################ #
var InkStaticJSON := load("res://addons/inkgd/runtime/static/json.gd") as GDScript
var InkStaticNativeFunctionCall := load("res://addons/inkgd/runtime/static/native_function_call.gd") as GDScript
# ############################################################################ #
# Signals
# ############################################################################ #
## Emitted when the runtime encountered an exception. Exception are not
## recoverable and may corrupt the state. They are the consequence of either
## a programmer error or a bug in the runtime.
signal exception_raised(message, stack_trace)
# ############################################################################ #
# Properties
# ############################################################################ #
# Skips saving global values that remain equal to the initial values that were
# declared in Ink.
var dont_save_default_values: bool = true
## Uses `assert` instead of `push_error` to report critical errors, thus
## making them more explicit during development.
var stop_execution_on_exception: bool = true
## Uses `assert` instead of `push_error` to report story errors, thus
## making them more explicit during development.
var stop_execution_on_error: bool = true
# ############################################################################ #
var should_pause_execution_on_runtime_error: bool: get = get_speore, set = set_speore
func get_speore() -> bool:
printerr(
"'should_pause_execution_on_runtime_error' is deprecated, " +
"use 'stop_execution_on_exception' instead."
)
return stop_execution_on_exception
func set_speore(value: bool):
printerr(
"'should_pause_execution_on_runtime_error' is deprecated, " +
"use 'stop_execution_on_exception' instead."
)
stop_execution_on_exception = value
var should_pause_execution_on_story_error: bool: get = get_speose, set = set_speose
func get_speose() -> bool:
printerr(
"'should_pause_execution_on_story_error' is deprecated, " +
"use 'stop_execution_on_error' instead."
)
return stop_execution_on_error
func set_speose(value: bool):
printerr(
"'should_pause_execution_on_story_error' is deprecated, " +
"use 'stop_execution_on_error' instead."
)
stop_execution_on_error = value
# ############################################################################ #
# "Static" Properties
# ############################################################################ #
var native_function_call: InkStaticNativeFunctionCall = InkStaticNativeFunctionCall.new()
var json: InkStaticJSON = InkStaticJSON.new(native_function_call)
# ############################################################################ #
# Internal Properties
# ############################################################################ #
# Recorded exceptions don't emit the 'exception' signal, since they are
# expected to be processed by the story and emitted through 'on_error'.
var record_story_exceptions: bool = false
var current_story_exceptions: Array = []
var _argument_exception_raised: bool
var _exception_raised: bool
# ############################################################################ #
# Overrides
# ############################################################################ #
func _init():
name = "__InkRuntime"
# ############################################################################ #
# Internal Methods
# ############################################################################ #
func clear_raised_exceptions() -> bool:
if _argument_exception_raised:
_argument_exception_raised = false
return true
if _argument_exception_raised:
_argument_exception_raised = false
return true
return false
func handle_exception(message: String) -> void:
var exception_message = "EXCEPTION: %s" % message
var stack_trace = _get_stack_trace()
_handle_generic_exception(
exception_message,
stop_execution_on_exception,
stack_trace
)
_exception_raised
emit_signal("exception_raised", exception_message, stack_trace)
func handle_argument_exception(message: String) -> void:
var exception_message = "ARGUMENT EXCEPTION: %s" % message
var stack_trace = _get_stack_trace()
_handle_generic_exception(
exception_message,
stop_execution_on_error,
stack_trace
)
_argument_exception_raised = true
emit_signal("exception_raised", exception_message, stack_trace)
func handle_story_exception(message: String, use_end_line_number: bool, metadata) -> void:
# When exceptions are "recorded", they are not reported immediately.
# 'Story' will take care of that at the end of the step.
if record_story_exceptions:
current_story_exceptions.append(StoryError.new(message, use_end_line_number, metadata))
else:
var exception_message = "STORY EXCEPTION: %s" % message
var stack_trace = _get_stack_trace()
_handle_generic_exception(exception_message, stop_execution_on_error, stack_trace)
emit_signal("exception_raised", exception_message, stack_trace)
# ############################################################################ #
# Private Methods
# ############################################################################ #
func _handle_generic_exception(
message: String,
should_pause_execution: bool,
stack_trace: PackedStringArray
) -> void:
if OS.is_debug_build():
if should_pause_execution:
assert(false, message)
elif Engine.is_editor_hint():
printerr(message)
if stack_trace.size() > 0:
printerr("Stack trace:")
for line in stack_trace:
printerr(line)
else:
push_error(message)
func _get_stack_trace() -> PackedStringArray:
var trace := PackedStringArray()
var i = 1
for stack_element in get_stack():
if i <= 3:
i += 1
continue
trace.append(str(
" ", (i - 3), " - ", stack_element["source"], ":",
stack_element["line"], " - at function: ", stack_element["function"]
))
i += 1
return trace

View file

@ -0,0 +1,623 @@
# ############################################################################ #
# Copyright © 2015-2021 inkle Ltd.
# Copyright © 2019-2022 Frédéric Maquin <fred@ephread.com>
# All Rights Reserved
#
# This file is part of inkgd.
# inkgd is licensed under the terms of the MIT license.
# ############################################################################ #
extends InkBase
class_name InkStaticJSON
# In the C# code this class has only static methods. In the GDScript, it will rather
# be a unique object, added to the InkRuntime singleton.
# ############################################################################ #
var InkValue = load("res://addons/inkgd/runtime/values/value.gd")
var InkStringValue = load("res://addons/inkgd/runtime/values/string_value.gd")
var InkDivertTargetValue = load("res://addons/inkgd/runtime/values/divert_target_value.gd")
var InkVariablePointerValue = load("res://addons/inkgd/runtime/values/variable_pointer_value.gd")
var InkListValue = load("res://addons/inkgd/runtime/values/list_value.gd")
var InkList = load("res://addons/inkgd/runtime/lists/ink_list.gd")
var InkListDefinition = load("res://addons/inkgd/runtime/lists/list_definition.gd")
var InkListDefinitionsOrigin = load("res://addons/inkgd/runtime/lists/list_definitions_origin.gd")
# ############################################################################ #
# (Array<Variant>, bool) -> Array
func jarray_to_runtime_obj_list(jarray: Array, skip_last = false) -> Array:
var count = jarray.size()
if skip_last:
count -= 1
var list = []
var i = 0
while (i < count):
var jtok = jarray[i]
var runtime_obj = jtoken_to_runtime_object(jtok)
list.append(runtime_obj)
i += 1
return list
# (self.Json.Writer, Dictionary<String, InkObject>) -> void
func write_dictionary_runtime_objs(writer, dictionary: Dictionary) -> void:
writer.write_object_start()
for key in dictionary:
writer.write_property_start(key)
write_runtime_object(writer, dictionary[key])
writer.write_property_end()
writer.write_object_end()
# (self.Json.Writer, Array<InkObject>) -> void
func write_list_runtime_objs(writer, list: Array) -> void:
writer.write_array_start()
for val in list:
write_runtime_object(writer, val)
writer.write_array_end()
# (self.Json.Writer, Array<Int>) -> void
func write_int_dictionary(writer, dict: Dictionary) -> void:
writer.write_object_start()
for key in dict:
writer.write_property(key, dict[key])
writer.write_object_end()
# (self.Json.Writer, InkObject) -> void
func write_runtime_object(writer, obj: InkObject) -> void:
var container = InkUtils.as_or_null(obj, "InkContainer")
if container:
write_runtime_container(writer, container)
return
var divert = InkUtils.as_or_null(obj, "Divert")
if divert:
var div_type_key = "->" # String
if divert.is_external:
div_type_key = "x()"
elif divert.pushes_to_stack:
if divert.stack_push_type == Ink.PushPopType.FUNCTION:
div_type_key = "f()"
elif divert.stackPushType == Ink.PushPopType.TUNNEL:
div_type_key = "->t->"
var target_str = null # String
if divert.has_variable_target:
target_str = divert.variable_divert_name
else:
target_str = divert.target_path_string
writer.write_object_start()
writer.write_property(div_type_key, target_str)
if divert.has_variable_target:
writer.write_property("var", true)
if divert.is_conditional:
writer.write_property("c", true)
if divert.external_args > 0:
writer.write_property("exArgs", divert.external_args)
writer.write_object_end()
return
var choice_point = InkUtils.as_or_null(obj, "ChoicePoint")
if choice_point:
writer.write_object_start()
writer.write_property("*", choice_point.path_string_on_choice)
writer.write_property("flg", choice_point.flags)
writer.write_object_end()
return
var bool_val = InkUtils.as_or_null(obj, "BoolValue")
if bool_val:
writer.write(bool_val.value)
return
var int_val = InkUtils.as_or_null(obj, "IntValue")
if int_val:
writer.write(int_val.value)
return
var float_val = InkUtils.as_or_null(obj, "FloatValue")
if float_val:
writer.write(float_val.value)
return
var str_val = InkUtils.as_or_null(obj, "StringValue")
if str_val:
if str_val.is_newline:
writer.write_string("\\n", false)
else:
writer.write_string_start()
writer.write_string_inner("^")
writer.write_string_inner(str_val.value)
writer.write_string_end()
return
var list_val = InkUtils.as_or_null(obj, "ListValue")
if list_val:
write_ink_list(writer, list_val)
return
var div_target_val = InkUtils.as_or_null(obj, "DivertTargetValue")
if div_target_val:
writer.write_object_start()
writer.write_property("^->", div_target_val.value.components_string)
writer.write_object_end()
return
var var_ptr_val = InkUtils.as_or_null(obj, "VariablePointerValue")
if var_ptr_val:
writer.write_object_start()
writer.write_property("^var", var_ptr_val.value)
writer.write_property("ci", var_ptr_val.context_index)
writer.write_object_end()
return
var glue = InkUtils.as_or_null(obj, "Glue")
if glue:
writer.write("<>")
return
var control_cmd = InkUtils.as_or_null(obj, "ControlCommand")
if control_cmd:
writer.write(self._control_command_names[control_cmd.command_type])
return
var native_func = InkUtils.as_or_null(obj, "NativeFunctionCall")
if native_func:
var name = native_func.name
if name == "^": name = "L^"
writer.write(name)
return
var var_ref = InkUtils.as_or_null(obj, "VariableReference")
if var_ref:
writer.write_object_start()
var read_count_path = var_ref.path_string_for_count
if read_count_path != null:
writer.write_property(["CNT?"], read_count_path)
else:
writer.write_property(["VAR?"], var_ref.name)
writer.write_object_end()
return
var var_ass = InkUtils.as_or_null(obj, "VariableAssignment")
if var_ass:
writer.write_object_start()
var key = "VAR=" if var_ass.is_global else "temp="
writer.write_property(key, var_ass.variable_name)
if !var_ass.is_new_declaration:
writer.write_property("re", true)
writer.write_object_end()
return
var void_obj = InkUtils.as_or_null(obj, "Void")
if void_obj:
writer.write("void")
return
# Legacy Tags (replaced in 1.1+)
var tag = InkUtils.as_or_null(obj, "Tag")
if tag:
writer.write_object_start()
writer.write_property("#", tag.text)
writer.write_object_end()
return
var choice = InkUtils.as_or_null(obj, "Choice")
if choice:
write_choice(writer, choice)
return
InkUtils.throw_exception("Failed to convert runtime object to Json token: %s" % obj)
return
# (Dictionary<String, Variant>) -> Dictionary<String, InkObject>
func jobject_to_dictionary_runtime_objs(jobject: Dictionary) -> Dictionary:
var dict = {}
for key in jobject:
dict[key] = jtoken_to_runtime_object(jobject[key])
return dict
# (Dictionary<String, Variant>) -> Dictionary<String, int>
func jobject_to_int_dictionary(jobject: Dictionary) -> Dictionary:
var dict = {}
for key in jobject:
dict[key] = int(jobject[key])
return dict
# (Variant) -> InkObject
func jtoken_to_runtime_object(token) -> InkObject:
if token is int || token is float || token is bool:
return InkValue.create(token)
if token is String:
var _str = token
var first_char = _str[0]
if first_char == "^":
return InkStringValue.new_with(_str.substr(1, _str.length() - 1))
elif first_char == "\n" && _str.length() == 1:
return InkStringValue.new_with("\n")
if _str == "<>": return InkGlue.new()
var i = 0
while (i < _control_command_names.size()):
var cmd_name = _control_command_names[i]
if _str == cmd_name:
return InkControlCommand.new(i)
i += 1
if _str == "L^": _str = "^"
if _static_native_function_call.call_exists_with_name(_str):
return InkNativeFunctionCall.call_with_name(_str, _static_native_function_call)
if _str == "->->":
return InkControlCommand.pop_tunnel()
elif _str == "~ret":
return InkControlCommand.pop_function()
if _str == "void":
return InkVoid.new()
if token is Dictionary:
var obj = token
var prop_value
if obj.has("^->"):
prop_value = obj["^->"]
return InkDivertTargetValue.new_with(
InkPath.new_with_components_string(str(prop_value))
)
if obj.has("^var"):
prop_value = obj["^var"]
var var_ptr = InkVariablePointerValue.new_with_context(str(prop_value))
if (obj.has("ci")):
prop_value = obj["ci"]
var_ptr.context_index = int(prop_value)
return var_ptr
var is_divert = false
var pushes_to_stack = false
var div_push_type = Ink.PushPopType.FUNCTION
var external = false
if obj.has("->"):
prop_value = obj["->"]
is_divert = true
elif obj.has("f()"):
prop_value = obj["f()"]
is_divert = true
pushes_to_stack = true
div_push_type = Ink.PushPopType.FUNCTION
elif obj.has("->t->"):
prop_value = obj["->t->"]
is_divert = true
pushes_to_stack = true
div_push_type = Ink.PushPopType.TUNNEL
elif obj.has("x()"):
prop_value = obj["x()"]
is_divert = true
external = true
pushes_to_stack = false
div_push_type = Ink.PushPopType.FUNCTION
if is_divert:
var divert = InkDivert.new()
divert.pushes_to_stack = pushes_to_stack
divert.stack_push_type = div_push_type
divert.is_external = external
var target = str(prop_value)
if obj.has("var"):
prop_value = obj["var"]
divert.variable_divert_name = target
else:
divert.target_path_string = target
divert.is_conditional = obj.has("c")
#if divert.is_conditional: prop_value = obj["c"]
if external:
if obj.has("exArgs"):
prop_value = obj["exArgs"]
divert.external_args = int(prop_value)
return divert
if obj.has("*"):
prop_value = obj["*"]
var choice = InkChoicePoint.new()
choice.path_string_on_choice = str(prop_value)
if obj.has("flg"):
prop_value = obj["flg"]
choice.flags = int(prop_value)
return choice
if obj.has("VAR?"):
prop_value = obj["VAR?"]
return InkVariableReference.new(str(prop_value))
elif obj.has("CNT?"):
prop_value = obj["CNT?"]
var read_count_var_ref = InkVariableReference.new()
read_count_var_ref.path_string_for_count = str(prop_value)
return read_count_var_ref
var is_var_ass = false
var is_global_var = false
if obj.has("VAR="):
prop_value = obj["VAR="]
is_var_ass = true
is_global_var = true
elif obj.has("temp="):
prop_value = obj["temp="]
is_var_ass = true
is_global_var = false
if is_var_ass:
var var_name = str(prop_value)
var is_new_decl = !obj.has("re")
var var_ass = InkVariableAssignment.new_with(var_name, is_new_decl)
var_ass.is_global = is_global_var
return var_ass
# Legacy Tags with texts (replaced in 1.1+)
if obj.has("#"):
prop_value = obj["#"]
return InkTag.new(str(prop_value))
if obj.has("list"):
prop_value = obj["list"]
var list_content = prop_value
var raw_list = InkList.new()
if obj.has("origins"):
prop_value = obj["origins"]
var names_as_objs = prop_value
raw_list.set_initial_origin_names(names_as_objs)
for name_to_val_key in list_content:
var item = InkListItem.new_with_full_name(name_to_val_key)
var val = list_content[name_to_val_key]
raw_list.set_item(item, val)
return InkListValue.new_with(raw_list)
if obj.has("originalChoicePath"):
return jobject_to_choice(obj)
if token is Array:
var container = jarray_to_container(token)
return container
if token == null:
return null
InkUtils.throw_exception("Failed to convert token to runtime object: %s" % str(token))
return null
# (self.Json.Writer, InkContainer, Bool) -> void
func write_runtime_container(writer, container: InkContainer, without_name = false) -> void:
writer.write_array_start()
for c in container.content:
write_runtime_object(writer, c)
var named_only_content = container.named_only_content
var count_flags = container.count_flags
var has_name_property = (container.name != null) && !without_name
var has_terminator = named_only_content != null || count_flags > 0 || has_name_property
if has_terminator:
writer.write_object_start()
if named_only_content != null:
for named_content_key in named_only_content:
var name = named_content_key
var named_container = InkUtils.as_or_null(named_only_content[named_content_key], "InkContainer")
writer.write_property_start(name)
write_runtime_container(writer, named_container, true)
writer.write_property_end()
if count_flags > 0:
writer.write_property("#f", count_flags)
if has_name_property:
writer.write_property("#n", container.name)
if has_terminator:
writer.write_object_end()
else:
writer.write_null()
writer.write_array_end()
# (Array<Variant>) -> InkContainer
func jarray_to_container(jarray: Array) -> InkContainer:
var container = InkContainer.new()
container.content = jarray_to_runtime_obj_list(jarray, true)
var terminating_obj = InkUtils.as_or_null(jarray.back(), "Dictionary") # Dictionary<string, Variant>
if terminating_obj != null:
var named_only_content = {} # new Dictionary<String, InkObject>
for key in terminating_obj:
if key == "#f":
container.count_flags = int(terminating_obj[key])
elif key == "#n":
container.name = str(terminating_obj[key])
else:
var named_content_item = jtoken_to_runtime_object(terminating_obj[key])
var named_sub_container = InkUtils.as_or_null(named_content_item, "InkContainer")
if named_sub_container:
named_sub_container.name = key
named_only_content[key] = named_content_item
container.named_only_content = named_only_content
return container
# (Dictionary<String, Variant>) -> Choice
func jobject_to_choice(jobj: Dictionary) -> InkChoice:
var choice = InkChoice.new()
choice.text = str(jobj["text"])
choice.index = int(jobj["index"])
choice.source_path = str(jobj["originalChoicePath"])
choice.original_thread_index = int(jobj["originalThreadIndex"])
choice.path_string_on_choice = str(jobj["targetPath"])
return choice
# (self.Json.Writer, Choice) -> Void
func write_choice(writer, choice: InkChoice) -> void:
writer.write_object_start()
writer.write_property("text", choice.text)
writer.write_property("index", choice.index)
writer.write_property("originalChoicePath", choice.source_path)
writer.write_property("originalThreadIndex", choice.original_thread_index)
writer.write_property("targetPath", choice.path_string_on_choice)
writer.write_object_end()
# (self.Json.Writer, ListValue) -> Void
func write_ink_list(writer, list_val):
var raw_list = list_val.value
writer.write_object_start()
writer.write_property_start("list")
writer.write_object_start()
for item_key in raw_list.raw_keys():
var item = InkListItem.from_serialized_key(item_key)
var item_val = raw_list.get_raw(item_key)
writer.write_property_name_start()
writer.write_property_name_inner(item.origin_name if item.origin_name else "?")
writer.write_property_name_inner(".")
writer.write_property_name_inner(item.item_name)
writer.write_property_name_end()
writer.write(item_val)
writer.write_property_end()
writer.write_object_end()
writer.write_property_end()
if raw_list.size() == 0 && raw_list.origin_names != null && raw_list.origin_names.size() > 0:
writer.write_property_start("origins")
writer.write_array_start()
for name in raw_list.origin_names:
writer.write(name)
writer.write_array_end()
writer.write_property_end()
writer.write_object_end()
# (ListDefinitionsOrigin) -> Dictionary<String, Variant>
func list_definitions_to_jtoken (origin):
var result = {} # Dictionary<String, Variant>
for def in origin.lists:
var list_def_json = {} # Dictionary<String, Variant>
for item_to_val_key in def.items:
var item = InkListItem.from_serialized_key(item_to_val_key)
var val = def.items[item_to_val_key]
list_def_json[item.item_name] = val
result[def.name] = list_def_json
return result
# (Variant) -> ListDefinitionsOrigin
func jtoken_to_list_definitions(obj):
var defs_obj = obj
var all_defs = [] # Array<ListDefinition>
for k in defs_obj:
var name = str(k) # String
var list_def_json = defs_obj[k] # Dictionary<String, Variant>
var items = {} # Dictionary<String, int>
for name_value_key in list_def_json:
items[name_value_key] = int(list_def_json[name_value_key])
var def = InkListDefinition.new(name, items)
all_defs.append(def)
return InkListDefinitionsOrigin.new(all_defs)
func _init(native_function_call):
_static_native_function_call = native_function_call
_control_command_names = []
_control_command_names.append("ev") # EVAL_START
_control_command_names.append("out") # EVAL_OUTPUT
_control_command_names.append("/ev") # EVAL_END
_control_command_names.append("du") # DUPLICATE
_control_command_names.append("pop") # POP_EVALUATED_VALUE
_control_command_names.append("~ret") # POP_FUNCTION
_control_command_names.append("->->") # POP_TUNNEL
_control_command_names.append("str") # BEGIN_STRING
_control_command_names.append("/str") # END_STRING
_control_command_names.append("nop") # NO_OP
_control_command_names.append("choiceCnt") # CHOICE_COUNT
_control_command_names.append("turn") # TURNS
_control_command_names.append("turns") # TURNS_SINCE
_control_command_names.append("readc") # READ_COUNT
_control_command_names.append("rnd") # RANDOM
_control_command_names.append("srnd") # SEED_RANDOM
_control_command_names.append("visit") # VISIT_INDEX
_control_command_names.append("seq") # SEQUENCE_SHUFFLE_INDEX
_control_command_names.append("thread") # START_THREAD
_control_command_names.append("done") # DONE
_control_command_names.append("end") # END
_control_command_names.append("listInt") # LIST_FROM_INT
_control_command_names.append("range") # LIST_RANGE
_control_command_names.append("lrnd") # LIST_RANDOM
_control_command_names.append("#") # BEGIN_TAG
_control_command_names.append("/#") # END_TAG
var i = 0
while i < InkControlCommand.CommandType.TOTAL_VALUES:
if _control_command_names[i] == null:
InkUtils.throw_exception("Control command not accounted for in serialisation")
i += 1
# Array<String>
var _control_command_names = null
# ############################################################################ #
# Eventually a pointer to InkRuntime.StaticJson
var _static_native_function_call = null

View file

@ -0,0 +1,284 @@
# warning-ignore-all:unused_class_variable
# ############################################################################ #
# Copyright © 2015-2021 inkle Ltd.
# Copyright © 2019-2022 Frédéric Maquin <fred@ephread.com>
# All Rights Reserved
#
# This file is part of inkgd.
# inkgd is licensed under the terms of the MIT license.
# ############################################################################ #
extends RefCounted
class_name InkStaticNativeFunctionCall
# ############################################################################ #
# Imports
# ############################################################################ #
const ValueType = preload("res://addons/inkgd/runtime/values/value_type.gd").ValueType
# ############################################################################ #
const ADD = "+"
const SUBTRACT = "-"
const DIVIDE = "/"
const MULTIPLY = "*"
const MOD = "%"
const NEGATE = "_"
const EQUALS = "=="
const GREATER = ">"
const LESS = "<"
const GREATER_THAN_OR_EQUALS = ">="
const LESS_THAN_OR_EQUALS = "<="
const NOT_EQUALS = "!="
const NOT = "!"
const AND = "&&"
const OR = "||"
const MIN = "MIN"
const MAX = "MAX"
const POW = "POW"
const FLOOR = "FLOOR"
const CEILING = "CEILING"
const INT = "INT"
const FLOAT = "FLOAT"
const HAS = "?"
const HASNT = "!?"
const INTERSECT = "^"
const LIST_MIN = "LIST_MIN"
const LIST_MAX = "LIST_MAX"
const ALL = "LIST_ALL"
const COUNT = "LIST_COUNT"
const VALUE_OF_LIST = "LIST_VALUE"
const INVERT = "LIST_INVERT"
# ############################################################################ #
var native_functions = null # Dictionary<String, String>
# ############################################################################ #
# (String) -> Bool
func call_exists_with_name(function_name):
generate_native_functions_if_necessary()
return native_functions.has(function_name)
# () -> void
func generate_native_functions_if_necessary():
if native_functions == null:
native_functions = {}
add_int_binary_op(ADD, "int_binary_op_add")
add_int_binary_op(SUBTRACT, "int_binary_op_substract")
add_int_binary_op(MULTIPLY, "int_binary_op_multiply")
add_int_binary_op(DIVIDE, "int_binary_op_divide")
add_int_binary_op(MOD, "int_binary_op_mod")
add_int_unary_op (NEGATE, "int_unary_op_negate")
add_int_binary_op(EQUALS, "int_binary_op_equals")
add_int_binary_op(GREATER, "int_binary_op_greater")
add_int_binary_op(LESS, "int_binary_op_less")
add_int_binary_op(GREATER_THAN_OR_EQUALS, "int_binary_op_greater_than_or_equals")
add_int_binary_op(LESS_THAN_OR_EQUALS, "int_binary_op_less_than_or_equals")
add_int_binary_op(NOT_EQUALS, "int_binary_op_not_equals")
add_int_unary_op (NOT, "int_unary_op_not")
add_int_binary_op(AND, "int_binary_op_and")
add_int_binary_op(OR, "int_binary_op_or")
add_int_binary_op(MAX, "int_binary_op_max")
add_int_binary_op(MIN, "int_binary_op_min")
add_int_binary_op(POW, "int_binary_op_pow")
add_int_unary_op (FLOOR, "int_unary_op_floor")
add_int_unary_op (CEILING, "int_unary_op_ceiling")
add_int_unary_op (INT, "int_unary_op_int")
add_int_unary_op (FLOAT, "int_unary_op_float")
add_float_binary_op(ADD, "float_binary_op_add")
add_float_binary_op(SUBTRACT, "float_binary_op_substract")
add_float_binary_op(MULTIPLY, "float_binary_op_multiply")
add_float_binary_op(DIVIDE, "float_binary_op_divide")
add_float_binary_op(MOD, "float_binary_op_mod")
add_float_unary_op (NEGATE, "float_unary_op_negate")
add_float_binary_op(EQUALS, "float_binary_op_equals")
add_float_binary_op(GREATER, "float_binary_op_greater")
add_float_binary_op(LESS, "float_binary_op_less")
add_float_binary_op(GREATER_THAN_OR_EQUALS, "float_binary_op_greater_than_or_equals")
add_float_binary_op(LESS_THAN_OR_EQUALS, "float_binary_op_less_than_or_equals")
add_float_binary_op(NOT_EQUALS, "float_binary_op_not_equals")
add_float_unary_op (NOT, "float_unary_op_not")
add_float_binary_op(AND, "float_binary_op_and")
add_float_binary_op(OR, "float_binary_op_or")
add_float_binary_op(MAX, "float_binary_op_max")
add_float_binary_op(MIN, "float_binary_op_min")
add_float_binary_op(POW, "float_binary_op_pow")
add_float_unary_op (FLOOR, "float_unary_op_floor")
add_float_unary_op (CEILING, "float_unary_op_ceiling")
add_float_unary_op (INT, "float_unary_op_int")
add_float_unary_op (FLOAT, "float_unary_op_float")
add_string_binary_op(ADD, "string_binary_op_add")
add_string_binary_op(EQUALS, "string_binary_op_equals")
add_string_binary_op(NOT_EQUALS, "string_binary_op_not_equals")
add_string_binary_op(HAS, "string_binary_op_has")
add_string_binary_op(HASNT, "string_binary_op_hasnt")
add_list_binary_op (ADD, "list_binary_op_add")
add_list_binary_op (SUBTRACT, "list_binary_op_substract")
add_list_binary_op (HAS, "list_binary_op_has")
add_list_binary_op (HASNT, "list_binary_op_hasnt")
add_list_binary_op (INTERSECT, "list_binary_op_intersect")
add_list_binary_op (EQUALS, "list_binary_op_equals")
add_list_binary_op (GREATER, "list_binary_op_greater")
add_list_binary_op (LESS, "list_binary_op_less")
add_list_binary_op (GREATER_THAN_OR_EQUALS, "list_binary_op_greater_than_or_equals")
add_list_binary_op (LESS_THAN_OR_EQUALS, "list_binary_op_less_than_or_equals")
add_list_binary_op (NOT_EQUALS, "list_binary_op_not_equals")
add_list_binary_op (AND, "list_binary_op_and")
add_list_binary_op (OR, "list_binary_op_or")
add_list_unary_op (NOT, "list_unary_op_not")
add_list_unary_op (INVERT, "list_unary_op_invert")
add_list_unary_op (ALL, "list_unary_op_all")
add_list_unary_op (LIST_MIN, "list_unary_op_list_min")
add_list_unary_op (LIST_MAX, "list_unary_op_list_max")
add_list_unary_op (COUNT, "list_unary_op_count")
add_list_unary_op (VALUE_OF_LIST, "list_unary_op_value_of_list")
add_op_to_native_func(EQUALS, 2, ValueType.DIVERT_TARGET,
"native_func_divert_targets_equal")
add_op_to_native_func(NOT_EQUALS, 2, ValueType.DIVERT_TARGET,
"native_func_divert_targets_not_equal")
# (String, int, ValueType, Variant)
func add_op_to_native_func(name, args, val_type, op):
var native_func = null # NativeFunctionCall
if native_functions.has(name):
native_func = native_functions[name]
else:
native_func = InkNativeFunctionCall.new_with_name_and_number_of_parameters(name, args, self)
native_functions[name] = native_func
native_func.add_op_func_for_type(val_type, op)
func add_int_binary_op(name, op_function_name):
add_op_to_native_func(name, 2, ValueType.INT, op_function_name)
func add_int_unary_op(name, op_function_name):
add_op_to_native_func(name, 1, ValueType.INT, op_function_name)
func add_float_binary_op(name, op_function_name):
add_op_to_native_func(name, 2, ValueType.FLOAT, op_function_name)
func add_float_unary_op(name, op_function_name):
add_op_to_native_func(name, 1, ValueType.FLOAT, op_function_name)
func add_string_binary_op(name, op_function_name):
add_op_to_native_func(name, 2, ValueType.STRING, op_function_name)
func add_list_binary_op(name, op_function_name):
add_op_to_native_func(name, 2, ValueType.LIST, op_function_name)
func add_list_unary_op(name, op_function_name):
add_op_to_native_func(name, 1, ValueType.LIST, op_function_name)
# ############################################################################ #
func int_binary_op_add(x, y): return x + y
func int_binary_op_substract(x, y): return x - y
func int_binary_op_multiply(x, y): return x * y
func int_binary_op_divide(x, y): return x / y
func int_binary_op_mod(x, y): return x % y
func int_unary_op_negate(x): return -x
func int_binary_op_equals(x, y): return x == y
func int_binary_op_greater(x, y): return x > y
func int_binary_op_less(x, y): return x < y
func int_binary_op_greater_than_or_equals(x, y): return x >= y
func int_binary_op_less_than_or_equals(x, y): return x <= y
func int_binary_op_not_equals(x, y): return x != y
func int_unary_op_not(x): return x == 0
func int_binary_op_and(x, y): return x != 0 && y != 0
func int_binary_op_or(x, y): return x != 0 || y != 0
func int_binary_op_max(x, y): return max(x, y)
func int_binary_op_min(x, y): return min(x, y)
func int_binary_op_pow(x, y): return pow(float(x), float(y))
func int_unary_op_floor(x): return x
func int_unary_op_ceiling(x): return x
func int_unary_op_int(x): return x
func int_unary_op_float(x): return float(x)
func float_binary_op_add(x, y): return x + y
func float_binary_op_substract(x, y): return x - y
func float_binary_op_multiply(x, y): return x * y
func float_binary_op_divide(x, y): return x / y
func float_binary_op_mod(x, y): return fmod(x, y)
func float_unary_op_negate(x): return -x
func float_binary_op_equals(x, y): return x == y
func float_binary_op_greater(x, y): return x > y
func float_binary_op_less(x, y): return x < y
func float_binary_op_greater_than_or_equals(x, y): return x >= y
func float_binary_op_less_than_or_equals(x, y): return x <= y
func float_binary_op_not_equals(x, y): return x != y
func float_unary_op_not(x): return x == 0.0
func float_binary_op_and(x, y): return x != 0.0 && y != 0.0
func float_binary_op_or(x, y): return x != 0.0 || y != 0.0
func float_binary_op_max(x, y): return max(x, y)
func float_binary_op_min(x, y): return min(x, y)
func float_binary_op_pow(x, y): return pow(x, y)
func float_unary_op_floor(x): return floor(x)
func float_unary_op_ceiling(x): return ceil(x)
func float_unary_op_int(x): return int(x)
func float_unary_op_float(x): return x
func string_binary_op_add(x, y): return str(x, y)
func string_binary_op_equals(x, y): return x == y
func string_binary_op_not_equals(x, y): return x != y
# Note: The Content Test (in) operator does not returns true when testing
# against the empty string, unlike the behaviour of the original C# runtime.
func string_binary_op_has(x, y): return y == "" || (y in x)
func string_binary_op_hasnt(x, y): return !(y in x) && y != ""
func list_binary_op_add(x, y): return x.union(y)
func list_binary_op_substract(x, y): return x.without(y)
func list_binary_op_has(x, y): return x.contains(y)
func list_binary_op_hasnt(x, y): return !x.contains(y)
func list_binary_op_intersect(x, y): return x.intersection(y)
func list_binary_op_equals(x, y): return x.equals(y)
func list_binary_op_greater(x, y): return x.greater_than(y)
func list_binary_op_less(x, y): return x.less_than(y)
func list_binary_op_greater_than_or_equals(x, y): return x.greater_than_or_equals(y)
func list_binary_op_less_than_or_equals(x, y): return x.less_than_or_equals(y)
func list_binary_op_not_equals(x, y): return !x.equals(y)
func list_binary_op_and(x, y): return x.size() > 0 && y.size() > 0
func list_binary_op_or(x, y): return x.size() > 0 || y.size() > 0
func list_unary_op_not(x): return 1 if x.size() == 0 else 0
func list_unary_op_invert(x): return x.inverse
func list_unary_op_all(x): return x.all
func list_unary_op_list_min(x): return x.min_as_list()
func list_unary_op_list_max(x): return x.max_as_list()
func list_unary_op_count(x): return x.size()
func list_unary_op_value_of_list(x): return x.max_item.value
func native_func_divert_targets_equal(d1, d2): return d1.equals(d2)
func native_func_divert_targets_not_equal(d1, d2): return !d1.equals(d2)