class Novika::Block

Overview

Blocks are fundamental to Novika.

They are a kind of AST node, they hold continuations and are continuations, they are arrays, stacks, and hash tables, all at the same time.

In this sense, blocks have roles. But any block can be any role, and change its role as often and whenever it wants or needs to.

Included Modules

Defined in:

novika/forms/block.cr

Constant Summary

MAX_COUNT_TO_S = 128

Maximum amount of forms to display in block string representation.

MAX_NESTED_COUNT_TO_S = 12

Maximum amount of forms to display in string representation of nested blocks.

Constructors

Class Method Summary

Instance Method Summary

Instance methods inherited from module Novika::ISubmittableStore

submit(name : Form, form : Form) submit, submit?(name : Form, form : Form) submit?

Class methods inherited from module Novika::ISubmittableStore

typedesc typedesc

Instance methods inherited from module Novika::IReadableStore

form_for(name : Form) : Form form_for, form_for?(name : Form) : Form | Nil form_for?, has_form_for?(name : Form) : Bool has_form_for?, opener?(name : Form) : Bool opener?, pusher?(name : Form) : Bool pusher?

Class methods inherited from module Novika::IReadableStore

typedesc typedesc

Instance methods inherited from module Novika::Form

a(type : T.class) : T forall T a, desc(io : IO)
desc : String
desc
, die(details : String) die, effect(io)
effect
effect
, on_open(engine : Engine) : self on_open, on_parent_open(engine : Engine) : self on_parent_open, onto(block : Block) : self onto, sel(a, b) sel, to_quote : Quote to_quote

Instance methods inherited from module Novika::Schedulable

schedule(engine : Engine, stack : Block) schedule, schedule!(engine : Engine, stack : Block) schedule!

Constructor Detail

def self.new(parent : Block | Nil = nil, prototype : Nil | Novika::Block = self, tape : Novika::Tape(Novika::Form) | Nil = nil, dict : Novika::IDict | Nil = nil) #

Class Method Detail

def self.typedesc #

def self.with(array : Array(Form), leaf : Bool | Nil = nil) #

Creates and returns an orphan block with array being its tape substrate's container. See Tape.for.


def self.with(form1 : Form, form2 : Form) #

Double-form optimized version of Block.with.


def self.with(form : Form) #

Single-form optimized version of Block.with.


Instance Method Detail

def ==(other) : Bool #

Loose equality: for two blocks to be loosely equal, their tapes and their dictionaries must be loosely equal.

Supports recursive (reflection) equality, e.g.:

[ ] $: a
a a shove
a first a = "=> true"

def a(type : T.class, _depth = 0) : T forall T #

Converts this block into the given type. Code execution may be required, hence the need for engine. If failed, same as Form#a.


def add(form : Form) : self #

Adds form to the tape.


def at(b : Int32, e : Int32) #

Returns a block of forms between b and e, both inclusive. Clamps b and e to bounds.


def at(name : Form, entry : Entry) : self #

Binds name to entry in this block's dictionary.


def at(name : Form, form : Form) : self #

Binds name to form in this block's dictionary.


def at(index : Int32) : Form #

Returns the form at index in the tape. Dies if index is out of bounds. See Tape#at?.


def at(name : String, desc = "a builtin", &code : Engine, Block -> ) : self #

Makes an OpenEntry called name for code wrapped in Builtin.


def at(name : Word, desc = "a builtin", &code : Engine, Block -> ) : self #

Makes an OpenEntry called name for code wrapped in Builtin.


def at?(index) #

Returns the form at index, or nil.


def befriend(other : Block) : self #

Adds other to the friendlist of this block.


def can_be?(type : T.class) : Bool forall T #

Returns whether this block implements hook(s) needed for behaving like type. See also: a(type).


def clear_entries : self #

Removes all owned dictionary entries in this block.


def count #

Returns the amount of forms in this block.


def cursor #

Returns the cursor position in this block.


def delete_at(index : Int32) : self #

Deletes the form at index. Does nothing if index is out of bounds.


def delete_entry(name : Form) : self #

Deletes the entry corresponding to name form from the dictionary of this block if it exists there. Otherwise, does nothing.


def delete_if(& : Form -> Bool) : self #

Yields forms from left to right until the block returns true for one, then deletes that form. If the block does not return true for any form, does nothing.


def desc(io : IO) #
Description copied from module Novika::Form

Appends a string description of this form to io.


def describe_with?(comment : String, force = false) : String | Nil #

Sets the block comment of this block to comment in case it doesn't have a comment already.

Setting the comment can also be forced by making force true.


def drop : Form #

Removes and returns the top form. Dies if none.


def dupe : self #

Duplicates the form before the cursor, dies if none.


def each(&) #

Yields all forms in this block, going from left to right.


def each_entry(&) #

Yields entry names and Entry objects from the dictionary of this block.


def each_entry_name(&) #

Yields entry name forms in this block's dictionary.


def each_entry_value(&) #

Yields entry value forms in this block's dictionary.


def each_friend(&) #

Yields friends of this block. Asserts each is a block, otherwise, dies (e.g. the user may have mistakenly added some other form).


def each_neighbor(payload : Block -> T | Nil, visited : BlockIdMap | Nil = nil) forall T #

Explores neighbor blocks of this block, calls payload with each such neighbor block. Records all neighbors it visited in visited.

Explicitly nested (marked as ExN1-2 in the diagram below) neighbor blocks are blocks found in the dictionary and tape of this block (marked as B in the diagram below).

Implicitly nested (marked as ImN1-4 in the diagram below) neighbor blocks are blocks in the tapes and dictionaries of explicitly nested neighbor blocks, and so on, recursively.

┌───────────────────────────────────────┐
│ B                                     │
│  ┌───────────────┐ ┌───────────────┐  │
│  │ ExN1          │ │ ExN2          │  │
│  │ ┌────┐ ┌────┐ │ │ ┌────┐ ┌────┐ │  │
│  │ │ImN1│ │ImN2│ │ │ │ImN3│ │ImN4│ │  │
│  │ └────┘ └────┘ │ │ └────┘ └────┘ │  │
│  │    ...    ... │ │    ...    ... │  │
│  └───────────────┘ └───────────────┘  │
│                                       │
└───────────────────────────────────────┘

def each_neighbor(visited : BlockIdMap | Nil = nil, &payload : Block -> T | Nil) forall T #

Explores neighbor blocks of this block, calls payload with each such neighbor block. Records all neighbors it visited in visited.

Explicitly nested (marked as ExN1-2 in the diagram below) neighbor blocks are blocks found in the dictionary and tape of this block (marked as B in the diagram below).

Implicitly nested (marked as ImN1-4 in the diagram below) neighbor blocks are blocks in the tapes and dictionaries of explicitly nested neighbor blocks, and so on, recursively.

┌───────────────────────────────────────┐
│ B                                     │
│  ┌───────────────┐ ┌───────────────┐  │
│  │ ExN1          │ │ ExN2          │  │
│  │ ┌────┐ ┌────┐ │ │ ┌────┐ ┌────┐ │  │
│  │ │ImN1│ │ImN2│ │ │ │ImN3│ │ImN4│ │  │
│  │ └────┘ └────┘ │ │ └────┘ └────┘ │  │
│  │    ...    ... │ │    ...    ... │  │
│  └───────────────┘ └───────────────┘  │
│                                       │
└───────────────────────────────────────┘

def each_occurrence_of(pattern : Form, &) #

Yields occurrences of the given pattern found in this block. Matching is done using loose equality #==(other).


def each_relative_fetch(fetcher : Block -> T | Nil, seen : BlockIdMap | Nil = nil, skip_self : Bool = false, history : Block | Nil = nil) : T | Nil forall T #

Explores this block's relatives, i.e., its vertical (parent) and horizontal (friend) hierarchy, calls fetcher on each relative. This process is also known as the exploration of the block graph, where this block is the origin of exploration.

If fetcher returns a value of type T (a non-nil) for the given block, exploration terminates. If fetcher returns nil, exploration continues.

The order of exploration is roughly as follows:

  • The first echelon is explored: the parents, friends, and friends of parents of this block are explored.

  • The second echelon is explored: the parents, friends, and friends of parents of the blocks in first echelon are explored by recursing on each, effectively allowing lookup that is unlimited in terms of depth.

seen can be used to disable exploration of specific blocks, also blocking off the exploration of their relatives (if they were not otherwise reached already).

skip_self can be set to true to disable calling fetcher for this block. Note that if this block is reached by other means (e.g. as in self -- other -- self), fetcher is still going to be called.

history, a block, can optionally be provided. It will hold all explored blocks leading to the "discovery" of T.


def each_relative_fetch(*args, **kwargs, &fetcher : Block -> T | Nil) : T | Nil forall T #

Explores this block's relatives, i.e., its vertical (parent) and horizontal (friend) hierarchy, calls fetcher on each relative. This process is also known as the exploration of the block graph, where this block is the origin of exploration.

If fetcher returns a value of type T (a non-nil) for the given block, exploration terminates. If fetcher returns nil, exploration continues.

The order of exploration is roughly as follows:

  • The first echelon is explored: the parents, friends, and friends of parents of this block are explored.

  • The second echelon is explored: the parents, friends, and friends of parents of the blocks in first echelon are explored by recursing on each, effectively allowing lookup that is unlimited in terms of depth.

seen can be used to disable exploration of specific blocks, also blocking off the exploration of their relatives (if they were not otherwise reached already).

skip_self can be set to true to disable calling fetcher for this block. Note that if this block is reached by other means (e.g. as in self -- other -- self), fetcher is still going to be called.

history, a block, can optionally be provided. It will hold all explored blocks leading to the "discovery" of T.


def effect(io) #
Description copied from module Novika::Form

Generates and returns a description for the stack effect of this form.

For blocks and builtins, tries to extract a ( ... -- ... ) (but see EFFECT_PATTERN) from their corresponding comment. If could not extract or no comment, returns 'a block' for blocks and 'native code' for builtins.


def eject : Form #

Drops and returns the form after the cursor. Dies if cursor is at the end.


def entry_count #

Returns the amount of entries owned by (defined in) this block.


def entry_for(name : Form) : Entry #

Returns the dictionary entry for name, or dies.

See each_relative for a detailed description of lookup order etc.


def entry_for?(name : Form) : Entry | Nil #

Returns the dictionary entry for name, or nil.

See each_relative for a detailed description of lookup order etc.


def flat_at?(name : Form) : Entry | Nil #

Returns the dictionary entry corresponding to name. Does not traverse the block hierarchy.


def flat_has?(name : Form) : Bool #

Returns whether this block's (and this block's only) dictionary has an entry corresponding to name.


def form_for?(name : Form) : Form | Nil #
Description copied from module Novika::IReadableStore

Returns the value form for an entry with the given name, or nil if no such entry exists.


def has_comment? : Bool #

Returns whether this block has a comment.


def has_dict? : Bool #

Returns whether this block has a dict.


def has_form_for?(name : Form) : Bool #
Description copied from module Novika::IReadableStore

Returns whether this store has an entry with the given name.


def has_friends? : Bool #

Returns whether this block has any friends.


def has_relatives? : Bool #

Returns whether this block has a parent, friends, or both.


def has_tape? : Bool #

Returns whether this block has a tape.


def import!(from donor : Block) : self #

Imports entries from donor to this block's dictionary by mutating this block's dictionary.


def includes?(other : Form) : Bool #

Returns whether the tape of this block includes other, as per loose equality #==(other).


def inject(form : Form) : self #

Adds form after the cursor.


def instance(parent new_parent : Block = self, shallow = false, __tr : BlockIdMap | Nil = nil) : Block #

Creates and returns an instance of this block, under the given parent.


def next? : Form | Nil #

See Tape#next?.


def on_open(engine : Engine, stack : Block = engine.stack) : self #

Schedules this block for execution in engine using the safe scheduling method (see Engine#schedule). Optionally, a stack block may be provided (otherwise, the engine's current stack is used).


def opener?(name : Form) : Bool #
Description copied from module Novika::IReadableStore

Returns whether name opens its value form, as defined in this store. Dies if name is not defined in this store.


def parent : Block | Nil #

Holds a reference to the parent block (them all in a linked list of ancestors).


def parent=(parent : Block | Nil) #

Holds a reference to the parent block (them all in a linked list of ancestors).


def parent? : Block | Nil | Nil #

Holds a reference to the parent block (them all in a linked list of ancestors).


def paste(forms : Block) #

Mutably adds forms before the cursor in forms block's tape after the cursor in this block's tape.


def path_to_entry?(name : Form) : Tuple(Entry, Block) | Nil #

Returns a tuple that consists of the dictionary entry corresponding to name, followed by the path block which holds all blocks leading to the entry.

Returns nil if name could not be found.

In general works like #entry_for and friends, the only difference being that it also tracks and returns the path. The latter makes this method slightly slower that #entry_for.


def prototype : Block #

Returns the prototype of this block. Block instances return their prototype (AST) blocks, AST blocks return themselves.


def prototype=(prototype : Block) #

Returns the prototype of this block. Block instances return their prototype (AST) blocks, AST blocks return themselves.


def prototype? : Block | Nil #

Returns the prototype of this block. Block instances return their prototype (AST) blocks, AST blocks return themselves.


def pusher?(name : Form) : Bool #
Description copied from module Novika::IReadableStore

Returns whether name pushes its value form, as defined in this store. Dies if name is not defined in this store.


def resub(other : Block) : self #

Replaces this block's tape with other's.


def reverse_each(&) #

Yields all forms in this block, going from right to left.


def schedule(engine : Engine, stack : Block) : self #

Schedules an instance of this block for execution, with stack set as the stack that will be used by the instance during execution.

Moves the cursor of the instance before the first form so that the entire block will be executed by engine.


def schedule!(engine : Engine, stack : Block) : self #

Schedules this block for execution, with stack set as the stack that will be used by this block during execution.

Moves the cursor before the first form so that the entire block will be executed by engine.


def shallow : Block #

Returns a shallow copy of this block.


def slice : Tuple(Block, Block) #

Slices this block at cursor. This results in two halves, which are consequently returned.


def slurp(source : String) : self #

Parses all forms in string source, and adds them to this block.


def sort_using!(&cmp : Form, Form -> Int32) : self #

Sorts this block's tape inplace, calls cmp comparator proc for each form pair for a comparison integer -1, 0, or 1.


def spot(io, vicinity = 10, colorful = true) #

Appends a string representation of this block to io in which only forms in the negative and positive vicinity of this block's cursor are present, and the word before the cursor is emphasized.

Does not respect MAX_COUNT_TO_S. Does not display quotes. Does not display nested blocks.


def submit?(name : Form, form : Form) #
Description copied from module Novika::ISubmittableStore

Submits value form to an entry with the given name. Returns nil if no such entry exists.


def swap : self #

Swaps two forms before the cursor, dies if none.


def thru : Form #

Returns form after cursor, and moves cursor past it.

Similar to #eject, but doesn't modify the block.


def to(index : Int32) : self #

Moves tape cursor to index. Dies if index is out of bounds. See Tape#to?.


def to_dict_block : Block #

Builds and returns a dictionary block for this block.

Dictionary block is an orphan block whose dictionary is a shallow copy of this block's dictionary; and whose tape is empty.


def to_quote : Quote #
Description copied from module Novika::Form

Returns this form's quote representation.


def to_s(io) #

def to_tape_block : Block #

Builds and returns a tape block for this block.

Tape block is an orphan block whose tape is a shallow copy of this block's tape; and whose dictionary is empty.


def top : Form #

Returns the top form, dies if none.


def top? : Form | Nil #

Returns the top form, or nil if none.


def unfriend(other : Block) : self #

Removes other from the friendlist of this block.