Class: Rubycc::Preprocess::ConstantExpressionParser

Inherits:
Object
  • Object
show all
Defined in:
lib/rubycc/preprocess/constant_expression.rb

Overview

Parses the controlling constant-expression of a "#if"/"#elif" (ISO C 6.10.1) into a Front::AST the shared ConstantEvaluator can fold. It runs over an already-neutralized Front::Token stream: "defined" has been folded to 1/0, macros have been expanded, and every surviving identifier has been turned into the integer 0 (6.10.1p4), so the only leaves that reach here are integer/character constants and parentheses.

The grammar admitted is exactly the integer constant-expression subset the preprocessor allows: unary "+ - ~ !", the binary arithmetic, shift, relational, equality and bitwise operators, "&&"/"||", and "?:". Cast, sizeof, comma and assignment are outside it; those spellings either arrive as a stray punctuator (comma, assignment) or, being keyword-spelled identifiers, have already collapsed to 0, and any leftover token is rejected. Rather than a method per precedence level, a single precedence table drives one climbing loop, so the whole binary grammar is one method.

Constant Summary collapse

BINARY_OPERATORS =

Each binary operator's binding power (higher binds tighter) and the AST::Binary op it lowers to; "&&" and "||" carry :logical markers since they build their own short-circuiting nodes rather than an AST::Binary.

{
  "*" => [11, :mul], "/" => [11, :div], "%" => [11, :mod],
  "+" => [10, :add], "-" => [10, :sub],
  "<<" => [9, :shl], ">>" => [9, :shr],
  "<" => [8, :lt], ">" => [8, :gt], "<=" => [8, :le], ">=" => [8, :ge],
  "==" => [7, :eq], "!=" => [7, :ne],
  "&" => [6, :and],
  "^" => [5, :xor],
  "|" => [4, :or],
  "&&" => [3, :logical_and],
  "||" => [2, :logical_or]
}.freeze
LOGICAL_OR_PRECEDENCE =

The lowest binding power in the table ("||"); the conditional operator sits just below it, so climbing from here collects every binary operator before "?:" is considered.

2
MAX_NESTING_DEPTH =

The recursion-depth ceiling, mirroring the main parser's guard: a hostile "#if (((...)))" or "#if !!!...1" would otherwise recurse until the Ruby stack overflows with a bare SystemStackError. This grammar is far lighter than the main parser's (a single precedence-climbing loop rather than a deep chain), so its stack gives out only around 2500 nested parentheses; 500 stops a pathological input after roughly 250 levels — an order of magnitude below that, and far above the trivial nesting any real controlling expression uses (well under ten).

500

Instance Method Summary collapse

Constructor Details

#initialize(tokens, directive) ⇒ ConstantExpressionParser

Returns a new instance of ConstantExpressionParser.



56
57
58
59
60
61
62
63
# File 'lib/rubycc/preprocess/constant_expression.rb', line 56

def initialize(tokens, directive)
  @tokens = tokens
  @pos = 0
  # The directive keyword ("if"/"elif"), used only to name diagnostics.
  @directive = directive
  # Live recursion depth, capped at MAX_NESTING_DEPTH by #with_nesting_guard.
  @depth = 0
end

Instance Method Details

#parseObject



65
66
67
68
69
70
71
72
# File 'lib/rubycc/preprocess/constant_expression.rb', line 65

def parse
  node = parse_conditional
  unless current.eof?
    raise_at(current, "extra tokens at end of ##{@directive} expression")
  end

  node
end