aboutsummaryrefslogtreecommitdiffstats
path: root/core/base
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-04-13 18:40:35 +0000
committers-ol <s-ol@users.noreply.github.com>2020-04-14 08:46:54 +0000
commit1a8debe87762072b3a63b769aa515ebf63b4c70d (patch)
tree33b42866307d11d2c0878b23e0ec0bb8925d3fcc /core/base
parentspec base.match __tostring (diff)
downloadalive-1a8debe87762072b3a63b769aa515ebf63b4c70d.tar.gz
alive-1a8debe87762072b3a63b769aa515ebf63b4c70d.zip
move into proper Lua module (`alv`)
Diffstat (limited to 'core/base')
-rw-r--r--core/base/builtin.moon96
-rw-r--r--core/base/fndef.moon26
-rw-r--r--core/base/init.moon39
-rw-r--r--core/base/input.moon146
-rw-r--r--core/base/match.moon302
-rw-r--r--core/base/op.moon168
6 files changed, 0 insertions, 777 deletions
diff --git a/core/base/builtin.moon b/core/base/builtin.moon
deleted file mode 100644
index fd36c69..0000000
--- a/core/base/builtin.moon
+++ /dev/null
@@ -1,96 +0,0 @@
-----
--- Builtin / Special Form evaluation Strategy (`builtin`).
---
--- Responsible for quoting/evaluating subexpressions, instantiating and setting
--- up `Op`s, updating the current `Scope`, etc.
--- See `builtin` and `invoke` for examples.
---
--- @classmod Builtin
-
-class Builtin
---- Builtin interface.
---
--- methods that have to be implemented by `Builtin` implementations.
--- @section interface
-
- --- create a new instance.
- --
- -- @tparam Cell cell the Cell to evaluate
- -- @tparam Value head the (`AST:eval`d) `head` of the Cell to evaluate
- new: (@cell, @head) =>
- @tag = @cell.tag
- @tag\register @
-
- --- perform the actual evaluation.
- --
- -- Implementations should:
- --
- -- - eval or quote `tail` values
- -- - perform scope effects
- -- - wrap all child-results
- --
- -- @tparam Scope scope the active scope
- -- @tparam {AST,...} tail the arguments to this expression
- -- @treturn Result the result of this evaluation
- eval: (scope, tail) => error "not implemented"
-
- --- free resources
- destroy: =>
-
- --- setup or copy state from previous instance of same type.
- --
- -- `prev` is only passed if Builtin types of prev and current expression match.
- -- Otherwise, or when no previous expression exists, `nil` is passed.
- --
- -- @tparam ?Builtin prev the previous Builtin instance
- setup: (prev) =>
-
- --- the `Cell` this Builtin was created for.
- -- @tfield Cell cell
-
- --- the evaluated head of `cell`.
- -- @tfield AST head
-
- --- the identity of `cell`.
- -- @tfield Tag tag
-
---- static functions
--- @section static
-
- --- create and setup a `Builtin` for a given tag, then evaluate it.
- --
- -- Create a new instance using `tag` and `head` and call `setup` on it.
- -- If a previous instance with the same `tag` exists and has the same `head`,
- -- it pass it to `setup`. Register the `Builtin` with `tag`, evaluate it
- -- and return the `Result`.
- --
- -- @tparam Cell cell the `Cell` being evaluated
- -- @tparam Scope scope the active scope
- -- @tparam Value head the (`AST:eval`d) head of the `Cell` being evaluated
- -- @treturn Result the result of evaluation
- @eval_cell: (cell, scope, head) =>
- last = cell.tag\last!
- compatible = last and (last.__class == @) and last.head == head
-
- L\trace if compatible
- "reusing #{last} for #{cell.tag} <#{@__name} #{head}>"
- else if last
- "replacing #{last} with new #{cell.tag} <#{@__name} #{head}>"
- else
- "initializing #{cell.tag} <#{@__name} #{head}>"
-
- builtin = @ cell, head
- if compatible
- builtin\setup last
- else
- last\destroy! if last
- builtin\setup nil
-
- builtin\eval scope, cell\tail!
-
- __tostring: => "<#{@@__name} #{@head}>"
- __inherited: (cls) => cls.__base.__tostring = @__tostring
-
-{
- :Builtin
-}
diff --git a/core/base/fndef.moon b/core/base/fndef.moon
deleted file mode 100644
index b79ad85..0000000
--- a/core/base/fndef.moon
+++ /dev/null
@@ -1,26 +0,0 @@
-----
--- user-function definition (`fndef`).
---
--- When called, expands to its body with params bound to the fn arguments (see
--- `invoke.fn_invoke`).
---
--- @classmod FnDef
-
-class FnDef
---- static functions
--- @section static
-
- --- create a new instance
- --
- -- @classmethod
- -- @tparam {Value,...} params (`AST:quote`d) naming the function parameters
- -- @tparam AST body (`AST:quote`d) expression the function evaluates to
- -- @tparam Scope scope the lexical scope the function was defined in (closure)
- new: (@params, @body, @scope) =>
-
- __tostring: =>
- "(fn (#{table.concat [p\stringify! for p in *@params], ' '}) ...)"
-
-{
- :FnDef
-}
diff --git a/core/base/init.moon b/core/base/init.moon
deleted file mode 100644
index 9153f79..0000000
--- a/core/base/init.moon
+++ /dev/null
@@ -1,39 +0,0 @@
-----
--- Base definitions for extensions.
---
--- This module exports the following classes and tables that extension modules
--- may need:
---
--- @module base
--- @see Op
--- @see Builtin
--- @see FnDef
--- @see Input
--- @see base.match.val
--- @see base.match.evt
--- @see ValueStream
--- @see EventStream
--- @see IOStream
--- @see Result
--- @see Error
-
-import Op from require 'core.base.op'
-import Builtin from require 'core.base.builtin'
-import FnDef from require 'core.base.fndef'
-import Input from require 'core.base.input'
-import val, evt from require 'core.base.match'
-import ValueStream, EventStream, IOStream from require 'core.stream'
-import Result from require 'core.result'
-import Error from require 'core.error'
-
-{
- :Op
- :Builtin
- :FnDef
- :Input
- :val, :evt
-
- -- redundant exports, to keep anything an extension might need in one import
- :ValueStream, :EventStream, :IOStream
- :Result, :Error
-}
diff --git a/core/base/input.moon b/core/base/input.moon
deleted file mode 100644
index bcf188e..0000000
--- a/core/base/input.moon
+++ /dev/null
@@ -1,146 +0,0 @@
-----
--- Update scheduling policy for `Op` arguments.
---
--- @classmod Input
-import ValueStream, EventStream, IOStream from require 'core.stream'
-import Result from require 'core.result'
-
-inherits = (klass, frm) ->
- assert klass, "cant find the ancestor of nil"
- return true if klass == frm
- while klass.__parent
- return true if klass.__parent == frm
- klass = klass.__parent
- false
-
-match_parent = (inst, map) ->
- klass = assert inst and inst.__class, "not an instance"
- if key = map[klass]
- return key
-
- while klass.__parent
- if key = map[klass.__parent]
- return key
-
- klass = klass.__parent
-
-local ColdInput, ValueInput, IOInput, mapping
-
-class Input
---- Input interface.
---
--- Methods that have to be implemented by `Input` implementations.
--- @section interface
-
- --- create a new Input.
- --
- -- @classmethod
- -- @tparam Stream stream
- new: (@stream) =>
- assert @stream, "nil passed to Input: #{value}"
-
- --- copy state from old instance (optional).
- --
- -- called by `Op:setup` with another `Input` instance or `nil` once this instance is
- -- registered. Must prepare this instance for `dirty`.
- --
- -- May enter a 'setup state' that is exited using `finish_setup`.
- --
- -- @tparam ?Input prev previous `Input` intance or nil
- setup: (prev) =>
-
- --- whether this input requires processing (optional).
- --
- -- must return a boolean indicating whether `Op`s that refer to this instance
- -- should be notified (via `Op:tick`). If not overwritten, delegates to
- -- `stream`:@{ValueStream:dirty|dirty}.
- --
- -- @treturn bool whether processing is necessary
- dirty: => @stream\dirty!
-
- --- leave setup state (optional).
- --
- -- called after the `Op` has completed (or skipped) its first `Op:tick` after
- -- `Op:setup`. Must prepare this instance for dataflow operation.
- finish_setup: =>
-
- --- unwrap to Lua value (optional).
- --
- -- @treturn any the raw Lua value
- unwrap: => @stream\unwrap!
-
- --- return the type name of this `Input` (optional).
- type: => @stream.type
-
- --- return the metatype name of this `Input` (optional).
- metatype: => @stream.metatype
-
- --- the current value
- --
- -- @tfield ValueStream stream
-
---- members
--- @section members
-
- --- alias for `unwrap`.
- __call: => @stream\unwrap!
-
- __tostring: => "#{@@__name}:#{@stream}"
- __inherited: (cls) =>
- cls.__base.__call = @__call
- cls.__base.__tostring = @__tostring
-
---- static functions
--- @section static
-
- --- Create a `cold` `Input`.
- --
- -- Never marked dirty. Use this for input streams that are only read when
- -- another `Input` is dirty.
- --
- -- @tparam Stream|Result value
- @cold: (value) ->
- if value.__class == Result
- value = assert value.value, "Input from result without value!"
- ColdInput value
-
- --- Create a `hot` `Input`.
- --
- -- Behaviour depends on what kind of `Stream` `value` is:
- --
- -- - `ValueStream`: Marked dirty for the eval-tick if old and new `ValueStream` differ.
- -- - `EventStream` and `IOStream`: Marked dirty only if the current `EventStream` is dirty.
- --
- -- This is the most common `Input` strategy.
- --
- -- @tparam Stream|Result value
- @hot: (value) ->
- if value.__class == Result
- value = assert value.value, "Input from result without value!"
-
- InputType = match_parent value, mapping
- assert InputType, "Input from unknown value: #{value}"
- InputType value
-
-class ColdInput extends Input
- dirty: => false
-
-class ValueInput extends Input
- setup: (old) => @dirty_setup = not old or @stream != old.stream
- finish_setup: => @dirty_setup = nil
- dirty: =>
- return @dirty_setup if @dirty_setup != nil
- @stream\dirty!
-
-class IOInput extends Input
- io: true
-
-mapping = {
- [ValueStream]: ValueInput
- [EventStream]: Input
- [IOStream]: IOInput
-}
-
-{
- :Input
-}
diff --git a/core/base/match.moon b/core/base/match.moon
deleted file mode 100644
index 700b50d..0000000
--- a/core/base/match.moon
+++ /dev/null
@@ -1,302 +0,0 @@
------
---- Pattern capturing for Op argument parsing.
---
--- There is only one basic buildings block for assembling patterns:
--- `Type`. It can match `ValueStream`s and `EventStream`s depending on its
--- metatype argument and can take an optional type name to match as an argument.
---
--- In addition to this primitive, the following modifiers are available:
--- `Repeat`, `Sequence`, `Choice`, and `Optional`. They can be used directly,
--- but there is also a number of shorthands for assembling patterns quickly:
---
--- - `val()` and `evt()`: Shorthands for `Type('value')` and `Type('event')`
--- - `val.num`: Shorthand for `Type('value', 'num')`
--- - `evt.str`: Shorthand for `Type('event', 'str')`
--- - `pat * 2`: Shorthand for `Repeat(pat, 1, 2)` (1-4 times `pat`)
--- - `pat * 0`: Shorthand for `Repeat(pat, 1, nil)` (1-* times `pat`)
--- - `pat ^ 2`: Shorthand for `Repeat(pat, 0, 2)` (0-4 times `pat`)
--- - `pat ^ 0`: Shorthand for `Repeat(pat, 0, nil)` (0-* times `pat`)
--- - `a + b + … + z`: Shorthand for `Sequence{ a, b, ..., z }`
--- - `a / b / … / z`: Shorthand for `Choice{ a, b, ..., z }`
--- - `-pat`: Shorthand for `Optional(pat)`
---
--- To perform the actual matching, call the `:match` method on a pattern and
--- pass a sequence of `Result`s. The method will either return the captured
--- `Result`s (or a table structuring them)
---
--- Any ambiguous pattern can be set to 'recall mode' by invoking it.
--- Recalling patterns will memorize the first Result they match, and
--- only match further Results of the same type. For example
---
--- arg = (val.num / val.str)!
--- pattern = arg + arg
---
--- ...will match either two numbers or two strings, but not one number and one
--- string. Recalling works on `Choice` and `Type` patterns. `Type` patterns
--- without a type (`val!` and `evt!`) always behave like this.
---
--- On `Sequence` patterns, a special method `:named` exists. It takes a
--- sequence of keys that are used instead of integers when constructing the
--- capture table:
---
--- pattern = (val.str + val.num):named('key', 'value')
--- pattern:match(...)
--- -- returns { {key='a', value=1}, {key='b', value=2}, ...}
---
--- @module base.match
-import Error from require 'core.error'
-import ValueStream, EventStream from require 'core.stream'
-
-local Repeat, Sequence, Choice, Optional
-
-typestr = (result) ->
- str = result\type!
- str ..= '!' if result\metatype! == 'event'
- str
-
-class Pattern
- match: (seq) =>
- @reset!
- num, cap = @capture seq, 1
- if num != #seq
- args = table.concat [typestr arg for arg in *seq], ' '
- msg = "couldn't match arguments (#{args}) against pattern #{@}"
- error Error 'argument', msg
- cap
-
- remember: (key) =>
- return true unless @recall
-
- @recalled or= key
- @recalled == key
-
- rep: (min, max) => Repeat @, min, max
-
- reset: => @recalled = nil
-
- __call: => @
- __mul: (num) => Repeat @, 1, if num != 0 then num
- __pow: (num) => Repeat @, 0, if num != 0 then num
- __add: (other) => Sequence { @, other }
- __div: (other) => Choice { @, other }
- __unm: => Optional @
-
- __inherited: (cls) =>
- cls.__base.__call or= @__call
- cls.__base.__mul or= @__mul
- cls.__base.__pow or= @__pow
- cls.__base.__add or= @__add
- cls.__base.__div or= @__div
- cls.__base.__unm or= @__unm
-
---- Base Stream Pattern.
---
--- When instantiated with `type`, only succeeds for `Stream`s whose value and
--- meta types match.
---
--- Otherwise, matches Streams based only on `metatype` for the first match, but
--- using both afterwards (recall mode).
---
--- @function Stream
--- @tparam string metatype "value" or "event"
--- @tparam ?string type type name
-class Type extends Pattern
- new: (@metatype, @type) =>
- @recall = not @type
-
- capture: (seq, i) =>
- return unless seq[i]
- type, mt = seq[i]\type!, seq[i]\metatype!
- return unless @metatype == mt
- match = if @type then type == @type else @remember type
- if match
- 1, seq[i]
-
- __tostring: =>
- str = @type or @metatype
- str ..= '!' if @metatype == 'event'
- str
-
---- Repeat a pattern.
---
--- Matches a given `inner` pattern as many times as possible, within the given
--- minimum/maximum counts. Matching this pattern results in a sequence of the
--- individual captures produced by the inner pattern.
---
--- @function Repeat
--- @tparam Pattern inner the original pattern
--- @tparam ?number min minimum amount of repetitions
--- @tparam ?number max maximum amount of repetitions (default infinite)
-class Repeat extends Pattern
- new: (@inner, @min, @max) =>
-
- capture: (seq, i) =>
- take, all = 0, {}
- while true
- num, cap = @inner\capture seq, i+take
- break unless num
-
- take += num
- table.insert all, cap
-
- break if @max and take >= @max
-
- return if @min and take < @min
- return if @max and take > @max
-
- take, all
-
- reset: =>
- @inner\reset!
-
- __call: =>
- @@ @inner!, @min, @max
-
- __tostring: =>
- min = @min or '0'
- max = @max or '*'
- "#{@inner}{#{min}-#{max}}"
-
---- Match multiple patterns in order.
---
--- Matches the inner patterns in order, only succeeds if all of them match.
--- Captures the individual captures produced by the inner patterns in a
--- sequence, or table with keys specified in `keys` or using the `:named(...)`
--- modifier.
---
--- @function Sequence
--- @tparam {Pattern,...} elements the inner patterns
--- @tparam ?{string,...} keys the keys to use when capturing matches
-class Sequence extends Pattern
- new: (@elements, @keys) =>
-
- capture: (seq, i) =>
- take, all = 0, {}
- for key, elem in ipairs @elements
- num, cap = elem\capture seq, i+take
- return unless num
-
- take += num
- key = @keys[key] if @keys
- all[key] = cap
-
- take, all
-
- reset: =>
- for elem in *@elements
- elem\reset!
-
- named: (...) =>
- @@ [e for e in *@elements], { ... }
-
- __call: =>
- @@ [e! for e in *@elements]
-
- __add: (other) =>
- elements = [e for e in *@elements]
- table.insert elements, other
- @@ elements
-
- __tostring: =>
- core = table.concat [tostring e for e in *@elements], ' '
- "(#{core})"
-
---- Match one of multiple options.
---
--- Matches using the first matching pattern in `elements` and returns its
--- captured value. Supports recalling the matched subpattern.
---
--- @function Choice
--- @tparam {Pattern,...} elements the inner patterns
--- @tparam ?{string,...} keys the keys to use when capturing matches
-class Choice extends Pattern
- new: (@elements, @recall=false) =>
-
- capture: (seq, i) =>
- for key, elem in ipairs @elements
- num, cap = elem\capture seq, i
- if num and @remember key
- return num, cap
-
- reset: =>
- super!
- for elem in *@elements
- elem\reset!
-
- __call: =>
- @@ [e! for e in *@elements], true
-
- __div: (other) =>
- elements = [e for e in *@elements]
- table.insert elements, other
- @@ elements
-
- __tostring: =>
- core = table.concat [tostring e for e in *@elements], ' | '
- "(#{core})"
-
---- Optionally match a pattern.
---
--- Matches using the first matching pattern in `elements` and returns its
--- captured value. Supports recalling the matched subpattern.
---
--- @function Optional
--- @tparam {Pattern,...} elements the inner patterns
--- @tparam ?{string,...} keys the keys to use when capturing matches
-class Optional extends Pattern
- new: (@inner) =>
-
- capture: (seq, i) =>
- num, cap = @inner\capture seq, i
- num or 0, cap
-
- reset: =>
- @inner\reset!
-
- __call: =>
- @@ @inner!
-
- __unm: => @
-
- __tostring: => "#{@inner}?"
-
---- `Value` shorthands.
---
--- Call or index with a string to obtain a `Type` instance.
--- Call to obtain a wildcard pattern.
---
--- val.str, val.num
--- val['vec3'], val('vec3')
--- val()
---
--- @table val
-val = setmetatable {}, {
- __index: (key) =>
- with v = Type 'value', key
- @[key] = v
-
- __call: (...) => Type 'value', ...
-}
-
---- `Event` shorthands.
---
--- Call or index with a string to obtain an `Type` instance.
--- Call to obtain a wildcard pattern.
---
--- evt.bang, evt.str, evt.num
--- evt['midi/message'], evt('midi/message')
--- evt()
---
--- @table evt
-evt = setmetatable {}, {
- __index: (key) =>
- with v = Type 'event', key
- @[key] = v
-
- __call: (...) => Type 'event', ...
-}
-
-{
- :Type, :Repeat, :Sequence, :Choice, :Optional
- :val, :evt
-}
diff --git a/core/base/op.moon b/core/base/op.moon
deleted file mode 100644
index b0a83dc..0000000
--- a/core/base/op.moon
+++ /dev/null
@@ -1,168 +0,0 @@
-----
--- Persistent expression Operator.
---
--- @classmod Op
-
-deepcopy = (val) ->
- switch type val
- when 'number', 'string', 'boolean', 'nil'
- val
- when 'table'
- assert (not getmetatable {}), "state should only contain simple tables!"
- {(deepcopy k), (deepcopy v) for k,v in pairs val}
- else
- error "state cannot contain values of type '#{type val}'"
-
-class Op
---- members
--- @section members
-
- do_yield = (table) ->
- for k, v in pairs table
- if v.__class
- coroutine.yield v
- else
- do_yield v
- --- yield all `Input`s from the (potentially nested) `inputs` table
- --
- -- @treturn iterator iterator over `inputs`
- all_inputs: => coroutine.wrap -> do_yield @inputs
-
- --- create a mutable copy of this Op.
- --
- -- Used to wrap insulate eval-cycles from each other. The copy does not have
- -- `inputs` set, since it is expected that this is (re)set in `setup`.
- --
- -- @treturn Op
- fork: =>
- out = if @out then @out\fork!
- state = if @state then deepcopy @state
- @@ out, state
-
- --- internal state of this Op.
- --
- -- This may be any simple Lua value, including Lua tables, as long as it has
- -- no metatables, multiple references/loops, userdata etc.
- --
- -- @tfield table state
-
- --- `Stream` instance representing this Op's computed output value.
- --
- -- Must be set to a `Stream` instance once `setup` finishes. Must not change
- -- type, be removed or replaced outside of `new` and `setup`. If it is a
- -- `ValueStream`, it should have a value assigned via `set` or the
- -- constructor once `tick` is called the first time. If `out`'s value is not
- -- initialized in `new` or `setup`, the implementation must make sure
- -- `tick``(true)` is called at least on the first eval-cycle the Op goes
- -- through, e.g. by using an `Input.hot` with a `ValueStream`.
- --
- -- @tfield Stream out
-
- --- table containing `Input`s to this Op.
- --
- -- The `inputs` table can be nested with string or integer keys,
- -- but all leaf-entries must be `Input` instances. It must not contain loops
- -- or instances of other classes.
- --
- -- @tfield {Input,...} inputs
-
---- Op interface.
---
--- methods that have to be implemented by `Op` implementations.
--- @section interface
-
- --- construct a new instance.
- --
- -- The optional parameters `out` and `state` are used by `fork` to duplicate
- -- an instance. If the constructor is overriden, these parameters must be
- -- forwarded to the superconstructor unchanged.
- --
- -- @function new
- -- @classmethod
- -- @tparam ?Stream out `out`
- -- @tparam ?table state `state`
-
- --- parse arguments and patch self.
- --
- -- Called once every eval-cycle. `inputs` is a list of `Result`s that are the
- -- argument to this op. The `inputs` have to be wrapped in `Input` instances
- -- to define update behaviour. Use `base.match` to parse them, then delegate to
- -- `super:setup` to patch the `Input` instances.
- --
- -- @function setup
- -- @tparam {Result,...} inputs a sequence of `Result`s
- -- @tparam Scope scope the active scope
-
- --- handle incoming events and update `out` (optional).
- --
- -- Called once per frame if any `Input`s are dirty. Some `Input`s may have
- -- special behaviour immediately after `setup` that can cause them to become
- -- dirty at eval-time. In this case, an eval-time tick is executed. You can
- -- detect this using the `setup` parameter.
- --
- -- `tick` is called after `setup`. `tick` is not called immediately after
- -- `setup` if no `inputs` are dirty. Update `out` here.
- --
- -- @tparam bool setup whether this is an eval-time tick
- tick: =>
-
- --- called when the Op is destroyed (optional).
- destroy: =>
-
---- implementation utilities.
---
--- super-methods and utilities for use by implementations.
--- @section super
-
- --- if `type` is passed, an output stream is instantiated.
- -- if `init` is passed, the stream is initialized to that Lua value.
- -- it is okay not to use this and create the output stream in :setup() if the
- -- type is not known at this time.
- --
- -- @classmethod
- -- @tparam ?Stream out `out`
- -- @tparam ?table state `state`
- new: (@out, @state) =>
-
- do_setup = (old, cur) ->
- for k, cur_val in pairs cur
- old_val = old and old[k]
-
- -- are these inputs or nested tables?
- cur_plain = cur_val and not cur_val.__class
- old_plain = old_val and not old_val.__class
-
- if cur_plain and old_plain
- -- both are tables, recurse
- do_setup old_val, cur_val
- elseif not (cur_plain or old_plain)
- -- both are streams (or nil), setup them
- cur_val\setup old_val
- --- setup previous `inputs`, if any, with the new inputs, and write them to
- -- `inputs`. The `inputs` table can be nested with string or integer keys,
- -- but all leaf-entries must be `Input` instances. It must not contain loops
- -- or instances of other classes.
- --
- -- @tparam table inputs table of `Input`s
- setup: (inputs) =>
- old_inputs = @inputs
- @inputs = inputs
- do_setup old_inputs, @inputs
-
- do_unwrap = (value) ->
- if value.__class
- value\unwrap!
- else
- {k, do_unwrap v for k,v in pairs value}
- --- `\unwrap` all `Input`s in `@inputs` and return a table with the same
- -- shape.
- --
- -- @treturn table the values of all `Input`s
- unwrap_all: => do_unwrap @inputs
-
- __tostring: => "<op: #{@@__name}>"
- __inherited: (cls) => cls.__base.__tostring = @__tostring
-
-{
- :Op
-}