Module: Postsvg::Visitors::PsVisitor::ControlFlow

Included in:
Postsvg::Visitors::PsVisitor
Defined in:
lib/postsvg/visitors/ps_visitor/control_flow.rb

Overview

Control flow operator handlers. Each pops operands from the RUNTIME stack (not the AST), so chained execution behaves correctly. Bodies are Procedure values that descend_into_procedure walks in the current visitor context.

Constant Summary collapse

LOOP_LIMIT =
10_000

Instance Method Summary collapse

Instance Method Details

#descend_into_procedure(procedure) ⇒ Object



100
101
102
103
104
# File 'lib/postsvg/visitors/ps_visitor/control_flow.rb', line 100

def descend_into_procedure(procedure)
  return unless procedure.is_a?(Model::Literals::Procedure)

  procedure.body.each { |node| node.accept(self, nil) }
end

#visit_exec(_op, _ctx) ⇒ Object



84
85
86
87
88
89
90
91
92
# File 'lib/postsvg/visitors/ps_visitor/control_flow.rb', line 84

def visit_exec(_op, _ctx)
  operand = @stack.pop
  case operand
  when Model::Literals::Procedure
    descend_into_procedure(operand)
  else
    @stack << operand
  end
end

#visit_exit(_op, _ctx) ⇒ Object



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

def visit_exit(_op, _ctx)
  raise Postsvg::ExitSignal
end

#visit_for(_op, _ctx) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/postsvg/visitors/ps_visitor/control_flow.rb', line 47

def visit_for(_op, _ctx)
  body = @stack.pop
  limit = pop_runtime_number.to_f
  increment = pop_runtime_number.to_f
  initial = pop_runtime_number.to_f
  iterations = 0
  if increment.positive?
    value = initial
    while value <= limit
      @stack << value
      descend_into_procedure(body)
      value += increment
      iterations += 1
      break if iterations > LOOP_LIMIT
    end
  elsif increment.negative?
    value = initial
    while value >= limit
      @stack << value
      descend_into_procedure(body)
      value += increment
      iterations += 1
      break if iterations > LOOP_LIMIT
    end
  else
    raise RenderError, "for: zero increment"
  end
end

#visit_if(_op, _ctx) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/postsvg/visitors/ps_visitor/control_flow.rb', line 13

def visit_if(_op, _ctx)
  body = @stack.pop
  condition = @stack.pop
  return unless truthy?(condition)

  descend_into_procedure(body)
end

#visit_ifelse(_op, _ctx) ⇒ Object



21
22
23
24
25
26
27
28
29
30
# File 'lib/postsvg/visitors/ps_visitor/control_flow.rb', line 21

def visit_ifelse(_op, _ctx)
  else_body = @stack.pop
  if_body = @stack.pop
  condition = @stack.pop
  if truthy?(condition)
    descend_into_procedure(if_body)
  else
    descend_into_procedure(else_body)
  end
end

#visit_loop(_op, _ctx) ⇒ Object



38
39
40
41
42
43
44
45
# File 'lib/postsvg/visitors/ps_visitor/control_flow.rb', line 38

def visit_loop(_op, _ctx)
  body = @stack.pop
  LOOP_LIMIT.times do
    descend_into_procedure(body)
  rescue Postsvg::ExitSignal
    break
  end
end

#visit_quit(_op, _ctx) ⇒ Object



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

def visit_quit(_op, _ctx)
  raise Postsvg::QuitSignal
end

#visit_repeat(_op, _ctx) ⇒ Object



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

def visit_repeat(_op, _ctx)
  body = @stack.pop
  count = pop_runtime_number.to_i.clamp(0, LOOP_LIMIT)
  count.times { descend_into_procedure(body) }
end

#visit_stopped(_op, _ctx) ⇒ Object



94
95
96
97
98
# File 'lib/postsvg/visitors/ps_visitor/control_flow.rb', line 94

def visit_stopped(_op, _ctx)
  body = @stack.pop
  descend_into_procedure(body)
  @stack << false
end