Class: Postsvg::Visitors::PsVisitor

Inherits:
Object
  • Object
show all
Includes:
Arithmetic, Boolean, Color, Common, Container, ControlFlow, Device, Dictionary, Font, GraphicsState, Painting, Path, Stack, Transformations
Defined in:
lib/postsvg/visitors/ps_visitor.rb,
lib/postsvg/visitors/ps_visitor/font.rb,
lib/postsvg/visitors/ps_visitor/path.rb,
lib/postsvg/visitors/ps_visitor/color.rb,
lib/postsvg/visitors/ps_visitor/stack.rb,
lib/postsvg/visitors/ps_visitor/common.rb,
lib/postsvg/visitors/ps_visitor/device.rb,
lib/postsvg/visitors/ps_visitor/boolean.rb,
lib/postsvg/visitors/ps_visitor/painting.rb,
lib/postsvg/visitors/ps_visitor/container.rb,
lib/postsvg/visitors/ps_visitor/arithmetic.rb,
lib/postsvg/visitors/ps_visitor/dictionary.rb,
lib/postsvg/visitors/ps_visitor/control_flow.rb,
lib/postsvg/visitors/ps_visitor/graphics_state.rb,
lib/postsvg/visitors/ps_visitor/transformations.rb

Overview

Walks a Model::Program and emits SVG via a SvgBuilder. Holds a GraphicsStack (immutable snapshots), an operand stack (for PostScript execution semantics that aren't expressed directly in the operator node — e.g. user-defined procedures that call arithmetic), and a current-path accumulator.

Dispatch is per-category: a separate module mixed in for each Model::Operators::* namespace. New operator categories = new module + include line, not a switch edit.

Defined Under Namespace

Modules: Arithmetic, Boolean, Color, Common, Container, ControlFlow, Device, Dictionary, Font, GraphicsState, Painting, Path, Stack, Transformations Classes: PathState

Constant Summary collapse

MAX_RECURSION =
256

Constants included from ControlFlow

ControlFlow::LOOP_LIMIT

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Common

#lookup_dict, #normalize_key, #numeric_value, #string_value, #truthy?

Methods included from Container

#visit_anchorsearch, #visit_astore, #visit_cvs, #visit_forall, #visit_get, #visit_getinterval, #visit_length, #visit_put, #visit_putinterval, #visit_search, #visit_string, #visit_token

Methods included from Font

#visit_charpath, #visit_findfont, #visit_scalefont, #visit_setfont, #visit_show, #visit_stringwidth, #visit_xyshow

Methods included from Device

#visit_copypage, #visit_nulldevice, #visit_showpage

Methods included from ControlFlow

#descend_into_procedure, #visit_exec, #visit_exit, #visit_for, #visit_if, #visit_ifelse, #visit_loop, #visit_quit, #visit_repeat, #visit_stopped

Methods included from Dictionary

#dict_stack, #visit_begin, #visit_countdictstack, #visit_currentdict, #visit_def, #visit_dict, #visit_dictstack, #visit_end, #visit_known, #visit_load, #visit_maxlength, #visit_store

Methods included from Transformations

#visit_concat, #visit_currentmatrix, #visit_dtransform, #visit_idtransform, #visit_itransform, #visit_matrix, #visit_rotate, #visit_scale, #visit_setmatrix, #visit_transform, #visit_translate

Methods included from GraphicsState

#visit_grestore, #visit_grestoreall, #visit_gsave, #visit_setdash, #visit_setlinecap, #visit_setlinejoin, #visit_setlinewidth, #visit_setmiterlimit

Methods included from Color

#visit_setcmykcolor, #visit_setgray, #visit_sethsbcolor, #visit_setrgbcolor

Methods included from Painting

#visit_clip, #visit_eoclip, #visit_eofill, #visit_fill, #visit_stroke

Methods included from Path

#visit_arc, #visit_arcn, #visit_closepath, #visit_currentpoint, #visit_curveto, #visit_lineto, #visit_moveto, #visit_newpath, #visit_rcurveto, #visit_rlineto, #visit_rmoveto

Methods included from Boolean

#visit_and, #visit_bitshift, #visit_eq, #visit_false, #visit_ge, #visit_gt, #visit_le, #visit_lt, #visit_ne, #visit_not, #visit_or, #visit_true, #visit_xor

Methods included from Arithmetic

#visit_abs, #visit_add, #visit_atan, #visit_ceiling, #visit_cos, #visit_div, #visit_exp, #visit_floor, #visit_idiv, #visit_ln, #visit_log, #visit_mod, #visit_mul, #visit_neg, #visit_round, #visit_sin, #visit_sqrt, #visit_sub, #visit_truncate

Methods included from Stack

#visit_clear, #visit_count, #visit_dup, #visit_exch, #visit_index, #visit_pop, #visit_roll

Constructor Details

#initialize(builder:, graphics: GraphicsStack.new) ⇒ PsVisitor

Returns a new instance of PsVisitor.



34
35
36
37
38
39
40
41
# File 'lib/postsvg/visitors/ps_visitor.rb', line 34

def initialize(builder:, graphics: GraphicsStack.new)
  @builder = builder
  @graphics = graphics
  @stack = [] # operand stack for execution semantics
  @path = PathState.new
  @dict_stack = [{}] # runtime dict stack (mirrors PLRM dict stack)
  @recursion_depth = 0
end

Instance Attribute Details

#builderObject (readonly)

Returns the value of attribute builder.



32
33
34
# File 'lib/postsvg/visitors/ps_visitor.rb', line 32

def builder
  @builder
end

#graphicsObject (readonly)

Returns the value of attribute graphics.



32
33
34
# File 'lib/postsvg/visitors/ps_visitor.rb', line 32

def graphics
  @graphics
end

#pathObject (readonly)

Returns the value of attribute path.



32
33
34
# File 'lib/postsvg/visitors/ps_visitor.rb', line 32

def path
  @path
end

#stackObject (readonly)

Returns the value of attribute stack.



32
33
34
# File 'lib/postsvg/visitors/ps_visitor.rb', line 32

def stack
  @stack
end

Instance Method Details

#dispatch(method_name, operator, ctx) ⇒ Object

Public entry: dispatch an operator node. Called from Model::Operator#accept.



45
46
47
# File 'lib/postsvg/visitors/ps_visitor.rb', line 45

def dispatch(method_name, operator, ctx)
  public_send(method_name, operator, ctx)
end

#pop_numberObject

Helper for popping numbers off the operand stack.



104
105
106
107
# File 'lib/postsvg/visitors/ps_visitor.rb', line 104

def pop_number
  v = @stack.pop
  numeric_value(v)
end

#pop_valueObject



109
110
111
# File 'lib/postsvg/visitors/ps_visitor.rb', line 109

def pop_value
  @stack.pop || Model::Computed.new(operator_keyword: "(missing)")
end

#visit_array(node, _ctx) ⇒ Object



73
74
75
# File 'lib/postsvg/visitors/ps_visitor.rb', line 73

def visit_array(node, _ctx)
  @stack << node.elements
end

#visit_dictionary(node, _ctx) ⇒ Object



81
82
83
# File 'lib/postsvg/visitors/ps_visitor.rb', line 81

def visit_dictionary(node, _ctx)
  @stack << node
end

#visit_hex(node, _ctx) ⇒ Object



69
70
71
# File 'lib/postsvg/visitors/ps_visitor.rb', line 69

def visit_hex(node, _ctx)
  @stack << node
end

#visit_invoke_procedure(invocation, _ctx) ⇒ Object

InvokeProcedure: descend into the body with the current state.



91
92
93
94
95
96
97
98
99
100
101
# File 'lib/postsvg/visitors/ps_visitor.rb', line 91

def visit_invoke_procedure(invocation, _ctx)
  @recursion_depth += 1
  if @recursion_depth > MAX_RECURSION
    raise RecursionLimitError,
          "procedure #{invocation.name} exceeded recursion limit #{MAX_RECURSION}"
  end

  invocation.procedure.body.each { |node| node.accept(self, nil) }
ensure
  @recursion_depth -= 1
end

#visit_name(node, _ctx) ⇒ Object



61
62
63
# File 'lib/postsvg/visitors/ps_visitor.rb', line 61

def visit_name(node, _ctx)
  @stack << node
end

#visit_number(node, _ctx) ⇒ Object

Literal visits push themselves onto the operand stack.



57
58
59
# File 'lib/postsvg/visitors/ps_visitor.rb', line 57

def visit_number(node, _ctx)
  @stack << node.value
end

#visit_procedure(node, _ctx) ⇒ Object



77
78
79
# File 'lib/postsvg/visitors/ps_visitor.rb', line 77

def visit_procedure(node, _ctx)
  @stack << node
end

#visit_program(program) ⇒ Object

Visit a Program body.



50
51
52
53
54
# File 'lib/postsvg/visitors/ps_visitor.rb', line 50

def visit_program(program)
  program.body.each do |statement|
    statement.accept(self, nil)
  end
end

#visit_string_literal(node, _ctx) ⇒ Object



65
66
67
# File 'lib/postsvg/visitors/ps_visitor.rb', line 65

def visit_string_literal(node, _ctx)
  @stack << node.value
end

#visit_unknown(operator, _ctx) ⇒ Object

Default: unhandled operator becomes a comment.



86
87
88
# File 'lib/postsvg/visitors/ps_visitor.rb', line 86

def visit_unknown(operator, _ctx)
  @builder.comment("unhandled operator: #{operator.keyword}")
end