lib/stupidedi/parser/instruction.rb
# frozen_string_literal: true
module Stupidedi
using Refinements
module Parser
class Instruction
include Inspect
# The segment identifier to which this {Instruction} applies
#
# @return [Symbol]
attr_reader :segment_id
# The segment use contains helpful information about the context of the
# segment within a definition tree (eg the parent structure's definition).
# It also enumerates the allowed values for segment qualifiers, which is
# used to minimize non-determinism.
#
# @return [Schema::SegmentUse]
attr_reader :segment_use
# This indicates the number of levels to ascend and terminate within the
# tree before storing the segment.
#
# @return [Integer]
attr_reader :pop_count
# This controls the allowed order in which structures may occur, by
# indicating the number of instructions to remove from the beginning of
# the instruction table.
#
# Repeatable structures have a value that ensures that their instruction
# remains in the successor table.
#
# Sibling structures that have the same position (as defined by their
# {Schema::SegmentUse}) will have equal drop_count values such that all of
# the sibling instructions remain in the successor table when any one of
# them is executed.
#
# @return [Integer]
attr_reader :drop_count
# This indicates that a child node should be added to the tree, which
# will then contain the segment.
#
# When a segment indicates the start of a child structure, the class
# indicated by this attribute is expected to respond to `push` by
# creating a new {AbstractState}.
#
# @return [Zipper::AbstractCursor]
attr_reader :push
def initialize(segment_id, segment_use, pop, drop, push)
@segment_id, @segment_use, @pop_count, @drop_count, @push =
segment_id || segment_use.definition.id, segment_use, pop, drop, push
end
def hash
[Instruction, state].hash
end
# @return [Instruction]
def copy(changes = {})
Instruction.new \
changes.fetch(:segment_id, @segment_id),
changes.fetch(:segment_use, @segment_use),
changes.fetch(:pop_count, @pop_count),
changes.fetch(:drop_count, @drop_count),
changes.fetch(:push, @push)
end
# @return [void]
def pretty_print(q)
id = "% 3s" % @segment_id.to_s
unless @segment_use.nil?
width = 18
name = @segment_use.definition.name
# Truncate the segment name to `width` characters
if name.length > width - 2
id = id + ": #{name.slice(0, width - 2)}.."
else
id = id + ": #{name.ljust(width)}"
end
end
q.text "Instruction[#{"% 3s" % id}]"
q.group(6, "(", ")") do
q.breakable ""
unless @segment_use.nil?
q.text "position: #{@segment_use.position},"
q.breakable
end
q.text "pop: #{@pop_count},"
q.breakable
q.text "drop: #{@drop_count}"
end
end
private
def state
[@segment_id, @segment_use, @pop_count, @drop_count, @push].hash
end
end
end
end