module Rucc::Parser::Switch

Private Instance Methods

check_case_duplicates!() click to toggle source

C11 6.8.4.2p3: No two case constant expressions have the same value.

# File lib/rucc/parser/switch.rb, line 89
def check_case_duplicates!
  len = @cases.size
  x = @cases[len - 1]
  @cases[0..-2].each do |y|
    if (x.e < y.b) || (y.e < x.b)
      next
    end
    if (x.b == x.e)
      raise "duplicate case value: #{x.b}"
      # error("duplicate case value: %d", x->beg)
    end
    raise "duplicate case value: #{x.b} ... #{x.e}"
    # error("duplicate case value: #{x.b} ... #{x.e}")
  end
end
make_switch_jump(var, c) click to toggle source

@param [Node] var @param [Case] c @return [Node]

# File lib/rucc/parser/switch.rb, line 54
def make_switch_jump(var, c)
  if c.b == c.e
    cond = Node.ast_binop(Type::INT, OP::EQ, var, Node.ast_inttype(Type::INT, c.b))
  else
    # [GNU] case i ... j is compiled to if (i <= cond && cond <= j) goto <label>.
    x = Node.ast_binop(Type::INT, OP::LE, Node.ast_inttype(Type::INT, c.b), var)
    y = Node.ast_binop(Type::INT, OP::LE, var, Node.ast_inttype(Type::INT, c.e))
    cond = Node.ast_binop(Type::INT, OP::LOGAND, x, y)
  end
  Node.ast_if(cond, Node.ast_jump(c.label), nil)
end
read_case_label(tok) click to toggle source

@return [Node]

# File lib/rucc/parser/switch.rb, line 67
def read_case_label(tok)
  if !@cases
    Util.errort!(tok, "stray case label")
  end
  label = @label_gen.next
  b = read_intexpr
  if next_token?(K::ELLIPSIS)
    e = read_intexpr
    expect!(':')
    if b > e
      Util.errort!(tok, "case region is not in correct order: #{b} ... #{e}")
    end
    @cases.push(Case.make_case(b, e, label))
  else
    expect!(':')
    @cases.push(Case.make_case(b, b, label))
  end
  check_case_duplicates!
  read_label_tail(Node.ast_dest(label))
end
read_default_label(tok) click to toggle source

@param [Token] tok @return [Node]

# File lib/rucc/parser/switch.rb, line 107
def read_default_label(tok)
  expect!(':');
  if @defaultcase
    Util.errort!(tok, "duplicate default")
  end
  @defaultcase = @label_gen.next
  read_label_tail(Node.ast_dest(@defaultcase))
end
read_switch_stmt() click to toggle source

@return [Node]

# File lib/rucc/parser/switch.rb, line 10
def read_switch_stmt
  expect!('(')
  expr = Node.conv(read_expr)
  ensure_inttype!(expr)
  expect!(')')

  e = @label_gen.next
  v = []
  with_switch_context(e) do
    body = read_stmt
    var = Node.ast_lvar(expr.ty, @tempname_gen.next, @localenv, @localvars)
    v.push(Node.ast_binop(expr.ty, '=', var, expr))
    @cases.each do |c|
      v.push(make_switch_jump(var, c))
    end
    v.push(Node.ast_jump(@defaultcase ? @defaultcase : e))
    if body
      v.push(body)
    end
    v.push(Node.ast_dest(e))
  end
  Node.ast_compound_stmt(v)
end
with_switch_context(brk) { || ... } click to toggle source
# File lib/rucc/parser/switch.rb, line 34
def with_switch_context(brk, &block)
  # Set switch context
  ocases       = @cases
  odefaultcase = @defaultcase
  obreak       = @lbreak
  @cases       = []
  @defaultcase = nil
  @lbreak      = brk

  yield

  # Restore switch context
  @cases       = ocases
  @defaultcase = odefaultcase
  @lbreak      = obreak
end