rucc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +55 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +46 -0
- data/LICENCE +21 -0
- data/README.md +82 -0
- data/Rakefile +2 -0
- data/Vagrantfile +10 -0
- data/bin/console +10 -0
- data/bin/rspec +2 -0
- data/bin/setup +8 -0
- data/exe/rucc +7 -0
- data/include/8cc.h +48 -0
- data/include/float.h +44 -0
- data/include/iso646.h +20 -0
- data/include/rucc.h +2 -0
- data/include/stdalign.h +11 -0
- data/include/stdarg.h +52 -0
- data/include/stdbool.h +11 -0
- data/include/stddef.h +15 -0
- data/include/stdnoreturn.h +8 -0
- data/lib/rucc.rb +8 -0
- data/lib/rucc/case.rb +22 -0
- data/lib/rucc/decl.rb +9 -0
- data/lib/rucc/enc.rb +9 -0
- data/lib/rucc/engine.rb +138 -0
- data/lib/rucc/file_io.rb +108 -0
- data/lib/rucc/file_io_list.rb +56 -0
- data/lib/rucc/gen.rb +1602 -0
- data/lib/rucc/int_evaluator.rb +114 -0
- data/lib/rucc/k.rb +73 -0
- data/lib/rucc/keyword.rb +17 -0
- data/lib/rucc/kind.rb +43 -0
- data/lib/rucc/label_gen.rb +13 -0
- data/lib/rucc/lexer.rb +40 -0
- data/lib/rucc/lexer/impl.rb +683 -0
- data/lib/rucc/lexer/preprocessor.rb +888 -0
- data/lib/rucc/lexer/preprocessor/cond_incl.rb +27 -0
- data/lib/rucc/lexer/preprocessor/constructor.rb +54 -0
- data/lib/rucc/lexer/preprocessor/pragma.rb +31 -0
- data/lib/rucc/lexer/preprocessor/special_macro.rb +110 -0
- data/lib/rucc/libc.rb +47 -0
- data/lib/rucc/m.rb +7 -0
- data/lib/rucc/macro.rb +24 -0
- data/lib/rucc/node.rb +530 -0
- data/lib/rucc/node/conv.rb +33 -0
- data/lib/rucc/op.rb +61 -0
- data/lib/rucc/operator.rb +13 -0
- data/lib/rucc/option.rb +30 -0
- data/lib/rucc/parser.rb +961 -0
- data/lib/rucc/parser/break.rb +18 -0
- data/lib/rucc/parser/builtin.rb +25 -0
- data/lib/rucc/parser/continue.rb +18 -0
- data/lib/rucc/parser/do.rb +33 -0
- data/lib/rucc/parser/ensure.rb +39 -0
- data/lib/rucc/parser/enum.rb +64 -0
- data/lib/rucc/parser/expr.rb +493 -0
- data/lib/rucc/parser/for.rb +71 -0
- data/lib/rucc/parser/func.rb +274 -0
- data/lib/rucc/parser/func_call.rb +54 -0
- data/lib/rucc/parser/goto.rb +29 -0
- data/lib/rucc/parser/if.rb +23 -0
- data/lib/rucc/parser/initializer.rb +237 -0
- data/lib/rucc/parser/label.rb +31 -0
- data/lib/rucc/parser/return.rb +16 -0
- data/lib/rucc/parser/struct_and_union.rb +280 -0
- data/lib/rucc/parser/switch.rb +117 -0
- data/lib/rucc/parser/while.rb +29 -0
- data/lib/rucc/pos.rb +11 -0
- data/lib/rucc/rmap.rb +22 -0
- data/lib/rucc/s.rb +9 -0
- data/lib/rucc/static_label_gen.rb +15 -0
- data/lib/rucc/t.rb +18 -0
- data/lib/rucc/tempname_gen.rb +14 -0
- data/lib/rucc/token.rb +114 -0
- data/lib/rucc/token_gen.rb +68 -0
- data/lib/rucc/type.rb +304 -0
- data/lib/rucc/type/check.rb +39 -0
- data/lib/rucc/type/conv.rb +29 -0
- data/lib/rucc/type_info.rb +21 -0
- data/lib/rucc/utf.rb +126 -0
- data/lib/rucc/util.rb +111 -0
- data/lib/rucc/version.rb +3 -0
- data/rucc.gemspec +38 -0
- metadata +201 -0
| @@ -0,0 +1,888 @@ | |
| 1 | 
            +
            require "set"
         | 
| 2 | 
            +
            require "rucc/m"
         | 
| 3 | 
            +
            require "rucc/macro"
         | 
| 4 | 
            +
            require "rucc/lexer/preprocessor/cond_incl"
         | 
| 5 | 
            +
            require "rucc/lexer/preprocessor/constructor"
         | 
| 6 | 
            +
            require "rucc/lexer/preprocessor/pragma"
         | 
| 7 | 
            +
            require "rucc/lexer/preprocessor/special_macro"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Rucc
         | 
| 10 | 
            +
              class Lexer
         | 
| 11 | 
            +
                class Preprocessor
         | 
| 12 | 
            +
                  include Constructor
         | 
| 13 | 
            +
                  include SpecialMacro
         | 
| 14 | 
            +
                  include Pragma
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # @param [Impl] impl
         | 
| 17 | 
            +
                  def initialize(impl)
         | 
| 18 | 
            +
                    @impl = impl
         | 
| 19 | 
            +
                    @std_include_path = []
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    # preprocessor context
         | 
| 22 | 
            +
                    @cond_incl_stack = []
         | 
| 23 | 
            +
                    @macros = {}
         | 
| 24 | 
            +
                    @once = {}
         | 
| 25 | 
            +
                    @include_guard = {}
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    # warning context
         | 
| 28 | 
            +
                    # TODO(south37) Impl warnf
         | 
| 29 | 
            +
                    @enable_warning = true
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    # Used for __DATE__ and __TIME__
         | 
| 32 | 
            +
                    @now = Time.now
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    define_special_macros!
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  # Return parsed node, only used for read_constexpr
         | 
| 38 | 
            +
                  attr_writer :expr_reader
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  # @return [Token]
         | 
| 41 | 
            +
                  def read_token
         | 
| 42 | 
            +
                    while true
         | 
| 43 | 
            +
                      tok = read_expand
         | 
| 44 | 
            +
                      if tok.bol && Token.is_keyword?(tok, '#') && (tok.hideset.size == 0)
         | 
| 45 | 
            +
                        read_directive(tok)
         | 
| 46 | 
            +
                        next
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                      Util.assert!{ !T::CPP_TOKENS.include?(tok.kind) }
         | 
| 49 | 
            +
                      return maybe_convert_keyword(tok)
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                    raise "Must not reach here!"
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  # @param [Token] tok
         | 
| 55 | 
            +
                  def unget_token(tok)
         | 
| 56 | 
            +
                    @impl.unget_token(tok)
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  # @return [Token]
         | 
| 60 | 
            +
                  def peek_token
         | 
| 61 | 
            +
                    r = read_token
         | 
| 62 | 
            +
                    unget_token(r)
         | 
| 63 | 
            +
                    r
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  # @param [String] path
         | 
| 67 | 
            +
                  def append_include_path(path)
         | 
| 68 | 
            +
                    @std_include_path << path
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                private
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  # @return [Token]
         | 
| 74 | 
            +
                  def read_expand
         | 
| 75 | 
            +
                    while true
         | 
| 76 | 
            +
                      tok = read_expand_newline
         | 
| 77 | 
            +
                      if tok.kind != T::NEWLINE
         | 
| 78 | 
            +
                        tok.expanded = true
         | 
| 79 | 
            +
                        return tok
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  # Note: This is "expand" function in the Dave Prosser's document.
         | 
| 85 | 
            +
                  # @return [Token]
         | 
| 86 | 
            +
                  def read_expand_newline
         | 
| 87 | 
            +
                    tok = @impl.lex
         | 
| 88 | 
            +
                    # NOTE: return tok if already expanded
         | 
| 89 | 
            +
                    if tok.expanded
         | 
| 90 | 
            +
                      return tok
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    if tok.kind != T::IDENT
         | 
| 94 | 
            +
                      return tok
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                    name = tok.sval
         | 
| 97 | 
            +
                    macro = @macros[name]
         | 
| 98 | 
            +
                    if !macro || tok.hideset.include?(name)
         | 
| 99 | 
            +
                      return tok
         | 
| 100 | 
            +
                    end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    case macro.kind
         | 
| 103 | 
            +
                    when M::OBJ
         | 
| 104 | 
            +
                      hideset = tok.hideset.dup
         | 
| 105 | 
            +
                      hideset << name
         | 
| 106 | 
            +
                      tokens = subst(macro, nil, hideset)
         | 
| 107 | 
            +
                      propagate_space(tokens, tok)
         | 
| 108 | 
            +
                      @impl.unget_all(tokens)
         | 
| 109 | 
            +
                      return read_expand
         | 
| 110 | 
            +
                    when M::FUNC
         | 
| 111 | 
            +
                      if !next?('(')
         | 
| 112 | 
            +
                        return tok
         | 
| 113 | 
            +
                      end
         | 
| 114 | 
            +
                      args = read_args(tok, macro)
         | 
| 115 | 
            +
                      rparen = peek_token
         | 
| 116 | 
            +
                      expect!(')')
         | 
| 117 | 
            +
                      hideset = ((tok.hideset & rparen.hideset) << name)
         | 
| 118 | 
            +
                      tokens = subst(macro, args, hideset)
         | 
| 119 | 
            +
                      propagate_space(tokens, tok)
         | 
| 120 | 
            +
                      @impl.unget_all(tokens)
         | 
| 121 | 
            +
                      return read_expand
         | 
| 122 | 
            +
                    when M::SPECIAL
         | 
| 123 | 
            +
                      macro.fn.call(tok)
         | 
| 124 | 
            +
                      return read_expand
         | 
| 125 | 
            +
                    else
         | 
| 126 | 
            +
                      raise "internal error"
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  # @param [Token] tok
         | 
| 131 | 
            +
                  # @return [Token]
         | 
| 132 | 
            +
                  def maybe_convert_keyword(tok)
         | 
| 133 | 
            +
                    return tok if tok.kind != T::IDENT
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    id = (K.keywords[tok.sval] || OP.operators[tok.sval])
         | 
| 136 | 
            +
                    return tok if id.nil?
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                    r = tok.dup
         | 
| 139 | 
            +
                    r.kind = T::KEYWORD
         | 
| 140 | 
            +
                    r.id = id
         | 
| 141 | 
            +
                    r
         | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                  # @param [Token] hash
         | 
| 145 | 
            +
                  def read_directive(hash)
         | 
| 146 | 
            +
                    tok = @impl.lex
         | 
| 147 | 
            +
                    if tok.kind == T::NEWLINE
         | 
| 148 | 
            +
                      return
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
                    if tok.kind == T::NUMBER
         | 
| 151 | 
            +
                      read_linemarker(tok)
         | 
| 152 | 
            +
                      return
         | 
| 153 | 
            +
                    end
         | 
| 154 | 
            +
                    if tok.kind != T::IDENT
         | 
| 155 | 
            +
                      raise "unsupported preprocessor directive: #{tok}"
         | 
| 156 | 
            +
                    end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                    # NOTE: only for debug
         | 
| 159 | 
            +
                    # print "#{' ' * 2 * @cond_incl_stack.size}##{tok.sval}\n"
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    case tok.sval
         | 
| 162 | 
            +
                    when "define"       then read_define
         | 
| 163 | 
            +
                    when "elif"         then read_elif(hash)
         | 
| 164 | 
            +
                    when "else"         then read_else(hash)
         | 
| 165 | 
            +
                    when "endif"        then read_endif(hash)
         | 
| 166 | 
            +
                    when "error"        then read_error(hash)
         | 
| 167 | 
            +
                    when "if"           then read_if
         | 
| 168 | 
            +
                    when "ifdef"        then read_ifdef
         | 
| 169 | 
            +
                    when "ifndef"       then read_ifndef
         | 
| 170 | 
            +
                    when "import"       then read_include(hash, tok.file, true)
         | 
| 171 | 
            +
                    when "include"      then read_include(hash, tok.file, false)
         | 
| 172 | 
            +
                    when "include_next" then read_include_next(hash, tok.file)
         | 
| 173 | 
            +
                    when "line"         then read_line
         | 
| 174 | 
            +
                    when "pragma"       then read_pragma
         | 
| 175 | 
            +
                    when "undef"        then read_undef
         | 
| 176 | 
            +
                    when "warning"      then read_warning(hash)
         | 
| 177 | 
            +
                    else
         | 
| 178 | 
            +
                      raise "unsupported preprocessor directive: #{tok}"
         | 
| 179 | 
            +
                    end
         | 
| 180 | 
            +
                  end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                  # GNU CPP outputs "# linenum filename flags" to preserve original
         | 
| 183 | 
            +
                  # source file information. This function reads them. Flags are ignored.
         | 
| 184 | 
            +
                  #
         | 
| 185 | 
            +
                  # @param [Token] tok
         | 
| 186 | 
            +
                  def read_linemarker(tok)
         | 
| 187 | 
            +
                    if !is_digit_sequence?(tok.sval)
         | 
| 188 | 
            +
                      Util.errort!(tok, "line number expected, but got #{tok}")
         | 
| 189 | 
            +
                    end
         | 
| 190 | 
            +
                    line = tok.sval.to_i
         | 
| 191 | 
            +
                    tok = @impl.lex
         | 
| 192 | 
            +
                    if tok.kind != T::STRING
         | 
| 193 | 
            +
                      Util.errort!(tok, "filename expected, but got #{tok}")
         | 
| 194 | 
            +
                    end
         | 
| 195 | 
            +
                    filename = tok.sval
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                    tok = @impl.lex
         | 
| 198 | 
            +
                    while tok.kind != T::NEWLINE
         | 
| 199 | 
            +
                      tok = @impl.lex
         | 
| 200 | 
            +
                    end
         | 
| 201 | 
            +
                    file = @impl.current_file
         | 
| 202 | 
            +
                    file.line = line
         | 
| 203 | 
            +
                    file.name = filename
         | 
| 204 | 
            +
                  end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                  # @param [Token] tok
         | 
| 207 | 
            +
                  # @param [Macro] macro
         | 
| 208 | 
            +
                  # @return [<Token>]
         | 
| 209 | 
            +
                  def read_args(tok, macro)
         | 
| 210 | 
            +
                    if (macro.nargs == 0) && Token.is_keyword?(peek_token, ')')
         | 
| 211 | 
            +
                      # If a macro M has no parameter, argument list of M()
         | 
| 212 | 
            +
                      # is an empty list. If it has one parameter,
         | 
| 213 | 
            +
                      # argument list of M() is a list containing an empty list.
         | 
| 214 | 
            +
                      return []
         | 
| 215 | 
            +
                    end
         | 
| 216 | 
            +
                    args = do_read_args(tok, macro)
         | 
| 217 | 
            +
                    if args.size != macro.nargs
         | 
| 218 | 
            +
                      Util.errort!(tok, "macro argument number does not match");
         | 
| 219 | 
            +
                    end
         | 
| 220 | 
            +
                    args
         | 
| 221 | 
            +
                  end
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                  # @param [Token] ident
         | 
| 224 | 
            +
                  # @param [Macro] macro
         | 
| 225 | 
            +
                  # @return [<Token>]
         | 
| 226 | 
            +
                  def do_read_args(ident, macro)
         | 
| 227 | 
            +
                    r = []
         | 
| 228 | 
            +
                    e = false
         | 
| 229 | 
            +
                    while !e
         | 
| 230 | 
            +
                      in_ellipsis = macro.is_varg && (r.size + 1 == macro.nargs)
         | 
| 231 | 
            +
                      arg, e = read_one_arg(ident, in_ellipsis)
         | 
| 232 | 
            +
                      r.push(arg)
         | 
| 233 | 
            +
                    end
         | 
| 234 | 
            +
                    if macro.is_varg && (r.size == macro.nargs - 1)
         | 
| 235 | 
            +
                      r.push([])
         | 
| 236 | 
            +
                    end
         | 
| 237 | 
            +
                    r
         | 
| 238 | 
            +
                  end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                  # @param [Token] ident
         | 
| 241 | 
            +
                  # @param [Boolean] readall
         | 
| 242 | 
            +
                  # @return [<<Token>, Boolean>]
         | 
| 243 | 
            +
                  def read_one_arg(ident, readall)
         | 
| 244 | 
            +
                    r = []
         | 
| 245 | 
            +
                    level = 0
         | 
| 246 | 
            +
                    while true
         | 
| 247 | 
            +
                      tok = @impl.lex
         | 
| 248 | 
            +
                      if tok.kind == T::EOF
         | 
| 249 | 
            +
                        Util.errort!(ident, "unterminated macro argument list")
         | 
| 250 | 
            +
                      end
         | 
| 251 | 
            +
                      if tok.kind == T::NEWLINE
         | 
| 252 | 
            +
                        next
         | 
| 253 | 
            +
                      end
         | 
| 254 | 
            +
                      if tok.bol && Token.is_keyword?(tok, '#')
         | 
| 255 | 
            +
                        read_directive(tok)
         | 
| 256 | 
            +
                        next
         | 
| 257 | 
            +
                      end
         | 
| 258 | 
            +
                      if (level == 0) && Token.is_keyword?(tok, ')')
         | 
| 259 | 
            +
                        unget_token(tok)
         | 
| 260 | 
            +
                        return r, true
         | 
| 261 | 
            +
                      end
         | 
| 262 | 
            +
                      if (level == 0) && Token.is_keyword?(tok, ',') && !readall
         | 
| 263 | 
            +
                        return r, false
         | 
| 264 | 
            +
                      end
         | 
| 265 | 
            +
                      if Token.is_keyword?(tok, '(')
         | 
| 266 | 
            +
                        level += 1
         | 
| 267 | 
            +
                      end
         | 
| 268 | 
            +
                      if Token.is_keyword?(tok, ')')
         | 
| 269 | 
            +
                        level -= 1
         | 
| 270 | 
            +
                      end
         | 
| 271 | 
            +
                      # C11 6.10.3p10: Within the macro argument list,
         | 
| 272 | 
            +
                      # newline is considered a normal whitespace character.
         | 
| 273 | 
            +
                      # I don't know why the standard specifies such a minor detail,
         | 
| 274 | 
            +
                      # but the difference of newline and space is observable
         | 
| 275 | 
            +
                      # if you stringize tokens using #.
         | 
| 276 | 
            +
                      if tok.bol
         | 
| 277 | 
            +
                        tok = copy_token(tok)
         | 
| 278 | 
            +
                        tok.bol = false
         | 
| 279 | 
            +
                        tok.space = true
         | 
| 280 | 
            +
                      end
         | 
| 281 | 
            +
                      r.push(tok)
         | 
| 282 | 
            +
                    end
         | 
| 283 | 
            +
                  end
         | 
| 284 | 
            +
             | 
| 285 | 
            +
                  # @param [Char] id
         | 
| 286 | 
            +
                  # @return [Boolean]
         | 
| 287 | 
            +
                  def next?(id)
         | 
| 288 | 
            +
                    tok = @impl.lex
         | 
| 289 | 
            +
                    if Token.is_keyword?(tok, id)
         | 
| 290 | 
            +
                      return true
         | 
| 291 | 
            +
                    end
         | 
| 292 | 
            +
                    @impl.unget_token(tok)
         | 
| 293 | 
            +
                    false
         | 
| 294 | 
            +
                  end
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                  # @param(return) [<Token>] tokens
         | 
| 297 | 
            +
                  # @param [Token] tmpl
         | 
| 298 | 
            +
                  def propagate_space(tokens, tmpl)
         | 
| 299 | 
            +
                    if tokens.size == 0
         | 
| 300 | 
            +
                      return
         | 
| 301 | 
            +
                    end
         | 
| 302 | 
            +
                    tok = copy_token(tokens.first)
         | 
| 303 | 
            +
                    tok.space = tmpl.space
         | 
| 304 | 
            +
                    tokens[0] = tok
         | 
| 305 | 
            +
                  end
         | 
| 306 | 
            +
             | 
| 307 | 
            +
                  # @param [Char] id
         | 
| 308 | 
            +
                  def expect!(id)
         | 
| 309 | 
            +
                    tok = @impl.lex
         | 
| 310 | 
            +
                    if !Token.is_keyword?(tok, id)
         | 
| 311 | 
            +
                      raise "#{id} expected, but got #{tok}"
         | 
| 312 | 
            +
                    end
         | 
| 313 | 
            +
                  end
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                  # @param [Macro] macro
         | 
| 316 | 
            +
                  # @param [Array] args
         | 
| 317 | 
            +
                  # @param [Set] hideset
         | 
| 318 | 
            +
                  # @return [<Token>]
         | 
| 319 | 
            +
                  def subst(macro, args, hideset)
         | 
| 320 | 
            +
                    r = []
         | 
| 321 | 
            +
                    i = 0
         | 
| 322 | 
            +
                    len = macro.body.size
         | 
| 323 | 
            +
                    while i < len
         | 
| 324 | 
            +
                      t0 = macro.body[i]
         | 
| 325 | 
            +
                      t1 = macro.body[i + 1]  # Note: nil when i == (macro.body.size - 1)
         | 
| 326 | 
            +
                      t0_param = (t0.kind == T::MACRO_PARAM)
         | 
| 327 | 
            +
                      t1_param = (t1 && t1.kind == T::MACRO_PARAM)
         | 
| 328 | 
            +
             | 
| 329 | 
            +
                      if Token.is_keyword?(t0, '#') && t1_param
         | 
| 330 | 
            +
                        r.push(stringize(t0, args[t1.position]))
         | 
| 331 | 
            +
                        i += 2
         | 
| 332 | 
            +
                        next
         | 
| 333 | 
            +
                      end
         | 
| 334 | 
            +
                      if Token.is_keyword?(t0, K::HASHHASH) && t1_param
         | 
| 335 | 
            +
                        arg = args[t1.position]
         | 
| 336 | 
            +
                        # [GNU] [,##__VA_ARG__] is expanded to the empty token sequence
         | 
| 337 | 
            +
                        # if __VA_ARG__ is empty. Otherwise it's expanded to
         | 
| 338 | 
            +
                        # [,<tokens in __VA_ARG__>].
         | 
| 339 | 
            +
                        if t1.is_vararg && (r.size > 0) && Token.is_keyword?(r.last, ',')
         | 
| 340 | 
            +
                          if arg.size > 0
         | 
| 341 | 
            +
                            r += arg
         | 
| 342 | 
            +
                          else
         | 
| 343 | 
            +
                            r.pop
         | 
| 344 | 
            +
                          end
         | 
| 345 | 
            +
                        elsif arg.size > 0
         | 
| 346 | 
            +
                          glue_push(r, arg.first)
         | 
| 347 | 
            +
                          arg[1..-1].each do |e|
         | 
| 348 | 
            +
                            r.push(e)
         | 
| 349 | 
            +
                          end
         | 
| 350 | 
            +
                        end
         | 
| 351 | 
            +
                        i += 2
         | 
| 352 | 
            +
                        next
         | 
| 353 | 
            +
                      end
         | 
| 354 | 
            +
                      if Token.is_keyword?(t0, K::HASHHASH) && t1
         | 
| 355 | 
            +
                        hideset = t1.hideset
         | 
| 356 | 
            +
                        glue_push(r, t1)
         | 
| 357 | 
            +
                        i += 2
         | 
| 358 | 
            +
                        next
         | 
| 359 | 
            +
                      end
         | 
| 360 | 
            +
                      if t0_param && t1 && Token.is_keyword?(t1, K::HASHHASH)
         | 
| 361 | 
            +
                        hideset = t1.hideset
         | 
| 362 | 
            +
                        arg = args[t0.position]
         | 
| 363 | 
            +
                        if arg.size == 0
         | 
| 364 | 
            +
                          i += 2
         | 
| 365 | 
            +
                          next
         | 
| 366 | 
            +
                        else
         | 
| 367 | 
            +
                          r += arg
         | 
| 368 | 
            +
                          i += 1
         | 
| 369 | 
            +
                          next
         | 
| 370 | 
            +
                        end
         | 
| 371 | 
            +
                      end
         | 
| 372 | 
            +
                      if t0_param
         | 
| 373 | 
            +
                        arg = args[t0.position]
         | 
| 374 | 
            +
                        r += expand_all(arg, t0)
         | 
| 375 | 
            +
                        i += 1
         | 
| 376 | 
            +
                        next
         | 
| 377 | 
            +
                      end
         | 
| 378 | 
            +
                      r.push(t0)
         | 
| 379 | 
            +
                      i += 1
         | 
| 380 | 
            +
                    end
         | 
| 381 | 
            +
                    add_hide_set(r, hideset)
         | 
| 382 | 
            +
                  end
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                  # @param(result) [<Token>] tokens
         | 
| 385 | 
            +
                  # @param [Token] tok
         | 
| 386 | 
            +
                  def glue_push(tokens, tok)
         | 
| 387 | 
            +
                    last = tokens.pop
         | 
| 388 | 
            +
                    tokens.push(glue_tokens(last, tok))
         | 
| 389 | 
            +
                  end
         | 
| 390 | 
            +
             | 
| 391 | 
            +
                  # @param [Token] t
         | 
| 392 | 
            +
                  # @param [Token] u
         | 
| 393 | 
            +
                  # @return [Token]
         | 
| 394 | 
            +
                  def glue_tokens(t, u)
         | 
| 395 | 
            +
                    b = "#{t}#{u}"
         | 
| 396 | 
            +
                    r = @impl.lex_string(b)
         | 
| 397 | 
            +
                    r
         | 
| 398 | 
            +
                  end
         | 
| 399 | 
            +
             | 
| 400 | 
            +
                  # @param [Token] tmpl
         | 
| 401 | 
            +
                  # @param [<Token>] args
         | 
| 402 | 
            +
                  def stringize(tmpl, args)
         | 
| 403 | 
            +
                    b = ""
         | 
| 404 | 
            +
                    args.each do |tok|
         | 
| 405 | 
            +
                      if b.size > 0 && tok.space
         | 
| 406 | 
            +
                        b << " "
         | 
| 407 | 
            +
                      end
         | 
| 408 | 
            +
                      b << tok.to_s
         | 
| 409 | 
            +
                    end
         | 
| 410 | 
            +
                    r = tmpl.dup
         | 
| 411 | 
            +
                    r.kind = T::STRING
         | 
| 412 | 
            +
                    r.sval = b
         | 
| 413 | 
            +
                    r.enc = ENC::NONE
         | 
| 414 | 
            +
                    r
         | 
| 415 | 
            +
                  end
         | 
| 416 | 
            +
             | 
| 417 | 
            +
                  # @return [<Token>]
         | 
| 418 | 
            +
                  def expand_all(tokens, tmpl)
         | 
| 419 | 
            +
                    @impl.token_buffer_stash(tokens.reverse)
         | 
| 420 | 
            +
                    r = []
         | 
| 421 | 
            +
                    while true
         | 
| 422 | 
            +
                      tok = read_expand
         | 
| 423 | 
            +
                      if tok.kind == T::EOF
         | 
| 424 | 
            +
                        break
         | 
| 425 | 
            +
                      end
         | 
| 426 | 
            +
                      r.push(tok)
         | 
| 427 | 
            +
                    end
         | 
| 428 | 
            +
                    propagate_space(r, tmpl)
         | 
| 429 | 
            +
                    @impl.token_buffer_unstash
         | 
| 430 | 
            +
                    r
         | 
| 431 | 
            +
                  end
         | 
| 432 | 
            +
             | 
| 433 | 
            +
                  # @param [<Token>] tokens
         | 
| 434 | 
            +
                  # @param [Set] hideset
         | 
| 435 | 
            +
                  # @return [<Token>]
         | 
| 436 | 
            +
                  def add_hide_set(tokens, hideset)
         | 
| 437 | 
            +
                    r = []
         | 
| 438 | 
            +
                    tokens.each do |token|
         | 
| 439 | 
            +
                      t = copy_token(token)
         | 
| 440 | 
            +
                      t.hideset = (t.hideset | hideset)
         | 
| 441 | 
            +
                      r.push(t)
         | 
| 442 | 
            +
                    end
         | 
| 443 | 
            +
                    r
         | 
| 444 | 
            +
                  end
         | 
| 445 | 
            +
             | 
| 446 | 
            +
                  # @param [Token] hash
         | 
| 447 | 
            +
                  # @param [FileIO] file
         | 
| 448 | 
            +
                  # @param [Boolean] isimport
         | 
| 449 | 
            +
                  def read_include(hash, file, isimport)
         | 
| 450 | 
            +
                    filename, std = read_cpp_header_name(hash)
         | 
| 451 | 
            +
                    expect_newline!
         | 
| 452 | 
            +
                    if filename[0] == '/'
         | 
| 453 | 
            +
                      if try_include("/", filename, isimport)
         | 
| 454 | 
            +
                        return
         | 
| 455 | 
            +
                      end
         | 
| 456 | 
            +
                      Util.errort!(hash, "cannot find header file: #{filename}")
         | 
| 457 | 
            +
                    end
         | 
| 458 | 
            +
                    if !std
         | 
| 459 | 
            +
                      dir = file.name ? File.dirname(file.name) : "."
         | 
| 460 | 
            +
                      if try_include(dir, filename, isimport)
         | 
| 461 | 
            +
                        return
         | 
| 462 | 
            +
                      end
         | 
| 463 | 
            +
                    end
         | 
| 464 | 
            +
                    @std_include_path.each do |path|
         | 
| 465 | 
            +
                      if try_include(path, filename, isimport)
         | 
| 466 | 
            +
                        return
         | 
| 467 | 
            +
                      end
         | 
| 468 | 
            +
                    end
         | 
| 469 | 
            +
                    Util.errort!(hash, "cannot find header file: #{filename}")
         | 
| 470 | 
            +
                  end
         | 
| 471 | 
            +
             | 
| 472 | 
            +
                  # @raise [RuntimeError]
         | 
| 473 | 
            +
                  def expect_newline!
         | 
| 474 | 
            +
                    tok = @impl.lex
         | 
| 475 | 
            +
                    if tok.kind != T::NEWLINE
         | 
| 476 | 
            +
                      raise "newline expected, but got #{tok}"
         | 
| 477 | 
            +
                    end
         | 
| 478 | 
            +
                  end
         | 
| 479 | 
            +
             | 
| 480 | 
            +
                  # @param [Token] hash
         | 
| 481 | 
            +
                  # @return [String, Boolean]
         | 
| 482 | 
            +
                  def read_cpp_header_name(hash)
         | 
| 483 | 
            +
                    # Try reading a filename using a special tokenizer for #include.
         | 
| 484 | 
            +
                    path, std = @impl.read_header_file_name
         | 
| 485 | 
            +
                    if path
         | 
| 486 | 
            +
                      return path, std
         | 
| 487 | 
            +
                    end
         | 
| 488 | 
            +
             | 
| 489 | 
            +
                    # If a token following #include does not start with < nor ",
         | 
| 490 | 
            +
                    # try to read the token as a regular token. Macro-expanded
         | 
| 491 | 
            +
                    # form may be a valid header file path.
         | 
| 492 | 
            +
                    tok = read_expand_newline
         | 
| 493 | 
            +
                    if tok.kind == T::NEWLINE
         | 
| 494 | 
            +
                      Util.errort!(hash, "expected filename, but got newline")
         | 
| 495 | 
            +
                    end
         | 
| 496 | 
            +
                    if tok.kind == T::STRING
         | 
| 497 | 
            +
                      std = false
         | 
| 498 | 
            +
                      return tok.sval, std
         | 
| 499 | 
            +
                    end
         | 
| 500 | 
            +
                    if !Token.is_keyword?(tok, '<')
         | 
| 501 | 
            +
                      Util.errort!(tok, "< expected, but got #{tok}")
         | 
| 502 | 
            +
                    end
         | 
| 503 | 
            +
                    tokens = []
         | 
| 504 | 
            +
                    while true
         | 
| 505 | 
            +
                      tok = read_expand_newline
         | 
| 506 | 
            +
                      if tok.kind == T::NEWLINE
         | 
| 507 | 
            +
                        Util.errort!(hash, "premature end of header name")
         | 
| 508 | 
            +
                      end
         | 
| 509 | 
            +
                      if Token.is_keyword?(tok, '>')
         | 
| 510 | 
            +
                        break
         | 
| 511 | 
            +
                      end
         | 
| 512 | 
            +
                      tokens.push(tok)
         | 
| 513 | 
            +
                    end
         | 
| 514 | 
            +
                    std = true
         | 
| 515 | 
            +
             | 
| 516 | 
            +
                    return tokens.join, std
         | 
| 517 | 
            +
                  end
         | 
| 518 | 
            +
             | 
| 519 | 
            +
                  # @param [String] dir
         | 
| 520 | 
            +
                  # @param [String] filename
         | 
| 521 | 
            +
                  # @param [Boolean] isimport
         | 
| 522 | 
            +
                  # @return [Boolean]
         | 
| 523 | 
            +
                  def try_include(dir, filename, isimport)
         | 
| 524 | 
            +
                    path = File.join(dir, filename)
         | 
| 525 | 
            +
                    if @once[path]
         | 
| 526 | 
            +
                      return true
         | 
| 527 | 
            +
                    end
         | 
| 528 | 
            +
                    if guarded?(path)
         | 
| 529 | 
            +
                      return true
         | 
| 530 | 
            +
                    end
         | 
| 531 | 
            +
             | 
| 532 | 
            +
                    # NOTE: file may not exist
         | 
| 533 | 
            +
                    begin
         | 
| 534 | 
            +
                      fp = File.open(path, "r")
         | 
| 535 | 
            +
                    rescue Errno::ENOENT, Errno::ENOTDIR
         | 
| 536 | 
            +
                      return false
         | 
| 537 | 
            +
                    end
         | 
| 538 | 
            +
             | 
| 539 | 
            +
                    if isimport
         | 
| 540 | 
            +
                      @once[path] = true
         | 
| 541 | 
            +
                    end
         | 
| 542 | 
            +
                    @impl.push_file(FileIO.new(fp, path))
         | 
| 543 | 
            +
                    true
         | 
| 544 | 
            +
                  end
         | 
| 545 | 
            +
             | 
| 546 | 
            +
                  CPP_TOKEN_ZERO = Token.new(T::NUMBER, sval: "0")
         | 
| 547 | 
            +
                  CPP_TOKEN_ONE  = Token.new(T::NUMBER, sval: "1")
         | 
| 548 | 
            +
             | 
| 549 | 
            +
                  # @param [String] path
         | 
| 550 | 
            +
                  # @return [Boolean]
         | 
| 551 | 
            +
                  def guarded?(path)
         | 
| 552 | 
            +
                    guard = @include_guard[path]
         | 
| 553 | 
            +
                    r = guard && @macros[guard]
         | 
| 554 | 
            +
                    define_obj_macro("__8cc_include_guard", r ? CPP_TOKEN_ONE : CPP_TOKEN_ZERO)
         | 
| 555 | 
            +
                    r
         | 
| 556 | 
            +
                  end
         | 
| 557 | 
            +
             | 
| 558 | 
            +
                  def read_line
         | 
| 559 | 
            +
                    tok = read_expand_newline
         | 
| 560 | 
            +
                    if (tok.kind != T::NUMBER) || !is_digit_sequence?(tok.sval)
         | 
| 561 | 
            +
                      Util.errort!(tok, "number expected after #line, but got #{tok}")
         | 
| 562 | 
            +
                    end
         | 
| 563 | 
            +
                    line = tok.sval.to_i
         | 
| 564 | 
            +
                    tok = read_expand_newline
         | 
| 565 | 
            +
                    if tok.kind == T::STRING
         | 
| 566 | 
            +
                      filename = tok.sval
         | 
| 567 | 
            +
                      expect_newline!
         | 
| 568 | 
            +
                    elsif tok.kind != T::NEWLINE
         | 
| 569 | 
            +
                      Util.errort!(tok, "newline or a source name are expected, but got #{tok}")
         | 
| 570 | 
            +
                    end
         | 
| 571 | 
            +
                    f = @impl.current_file
         | 
| 572 | 
            +
                    f.line = line
         | 
| 573 | 
            +
                    if filename
         | 
| 574 | 
            +
                      f.name = filename
         | 
| 575 | 
            +
                    end
         | 
| 576 | 
            +
                  end
         | 
| 577 | 
            +
             | 
| 578 | 
            +
                  # @param [String] p
         | 
| 579 | 
            +
                  # @return [Boolean]
         | 
| 580 | 
            +
                  def is_digit_sequence?(p)
         | 
| 581 | 
            +
                    p.match(/^\d*$/)
         | 
| 582 | 
            +
                  end
         | 
| 583 | 
            +
             | 
| 584 | 
            +
                  def read_if
         | 
| 585 | 
            +
                    do_read_if(read_constexpr)
         | 
| 586 | 
            +
                  end
         | 
| 587 | 
            +
             | 
| 588 | 
            +
                  # @return [Boolean]
         | 
| 589 | 
            +
                  def read_constexpr
         | 
| 590 | 
            +
                    @impl.token_buffer_stash(read_intexpr_line.reverse)
         | 
| 591 | 
            +
                    Util.assert!{ !@expr_reader.nil? }
         | 
| 592 | 
            +
                    expr = @expr_reader.call
         | 
| 593 | 
            +
                    tok = @impl.lex
         | 
| 594 | 
            +
                    if tok.kind != T::EOF
         | 
| 595 | 
            +
                      Util.errort!(tok, "stray token: #{tok}")
         | 
| 596 | 
            +
                    end
         | 
| 597 | 
            +
                    @impl.token_buffer_unstash
         | 
| 598 | 
            +
                    n, _ = IntEvaluator.eval(expr)
         | 
| 599 | 
            +
                    n != 0
         | 
| 600 | 
            +
                  end
         | 
| 601 | 
            +
             | 
| 602 | 
            +
                  def read_ifdef
         | 
| 603 | 
            +
                    tok = @impl.lex
         | 
| 604 | 
            +
                    if tok.kind != T::IDENT
         | 
| 605 | 
            +
                      Util.errort!(tok, "identifier expected, but got #{tok}")
         | 
| 606 | 
            +
                    end
         | 
| 607 | 
            +
                    do_read_if(@macros[tok.sval])
         | 
| 608 | 
            +
                  end
         | 
| 609 | 
            +
             | 
| 610 | 
            +
                  def read_ifndef
         | 
| 611 | 
            +
                    tok = @impl.lex
         | 
| 612 | 
            +
                    if tok.kind != T::IDENT
         | 
| 613 | 
            +
                      Util.errort!(tok, "identifier expected, but got #{tok}")
         | 
| 614 | 
            +
                    end
         | 
| 615 | 
            +
                    expect_newline!
         | 
| 616 | 
            +
                    do_read_if(!@macros[tok.sval])
         | 
| 617 | 
            +
                    if tok.count == 2
         | 
| 618 | 
            +
                      # "ifndef" is the second token in this file.
         | 
| 619 | 
            +
                      # Prepare to detect an include guard.
         | 
| 620 | 
            +
                      ci = @cond_incl_stack.last
         | 
| 621 | 
            +
                      ci.include_guard = tok.sval
         | 
| 622 | 
            +
                      ci.file = tok.file
         | 
| 623 | 
            +
                    end
         | 
| 624 | 
            +
                  end
         | 
| 625 | 
            +
             | 
| 626 | 
            +
                  # @param [Boolean] istrue
         | 
| 627 | 
            +
                  def do_read_if(istrue)
         | 
| 628 | 
            +
                    @cond_incl_stack.push(make_cond_incl(istrue))
         | 
| 629 | 
            +
             | 
| 630 | 
            +
                    if (!istrue)
         | 
| 631 | 
            +
                      @impl.skip_cond_incl!
         | 
| 632 | 
            +
                    end
         | 
| 633 | 
            +
                  end
         | 
| 634 | 
            +
             | 
| 635 | 
            +
                  def read_define
         | 
| 636 | 
            +
                    name = read_ident
         | 
| 637 | 
            +
                    tok = @impl.lex
         | 
| 638 | 
            +
                    if Token.is_keyword?(tok, '(') && !tok.space
         | 
| 639 | 
            +
                      read_funclike_macro(name)
         | 
| 640 | 
            +
                      return
         | 
| 641 | 
            +
                    end
         | 
| 642 | 
            +
                    @impl.unget_token(tok)
         | 
| 643 | 
            +
                    read_obj_macro(name.sval)
         | 
| 644 | 
            +
                  end
         | 
| 645 | 
            +
             | 
| 646 | 
            +
                  # @param [Token] name
         | 
| 647 | 
            +
                  def read_funclike_macro(name)
         | 
| 648 | 
            +
                    param = {}
         | 
| 649 | 
            +
                    param, isvarg = read_funclike_macro_params(name)
         | 
| 650 | 
            +
                    body = read_funclike_macro_body(param)
         | 
| 651 | 
            +
                    hashhash_check!(body)
         | 
| 652 | 
            +
                    macro = make_func_macro(body, param.size, isvarg)
         | 
| 653 | 
            +
                    @macros[name.sval] = macro
         | 
| 654 | 
            +
                  end
         | 
| 655 | 
            +
             | 
| 656 | 
            +
                  # @param [Token] name
         | 
| 657 | 
            +
                  # @return [<Hash, Boolean>]
         | 
| 658 | 
            +
                  def read_funclike_macro_params(name)
         | 
| 659 | 
            +
                    pos = 0
         | 
| 660 | 
            +
                    param = {}
         | 
| 661 | 
            +
                    while true
         | 
| 662 | 
            +
                      tok = @impl.lex
         | 
| 663 | 
            +
                      if Token.is_keyword?(tok, ')')
         | 
| 664 | 
            +
                        return param, false
         | 
| 665 | 
            +
                      end
         | 
| 666 | 
            +
                      if pos > 0
         | 
| 667 | 
            +
                        if !Token.is_keyword?(tok, ',')
         | 
| 668 | 
            +
                          Util.errort!(tok, ", expected, but got #{tok}")
         | 
| 669 | 
            +
                        end
         | 
| 670 | 
            +
                        tok = @impl.lex
         | 
| 671 | 
            +
                      end
         | 
| 672 | 
            +
                      if tok.kind == T::NEWLINE
         | 
| 673 | 
            +
                        Util.errort!(name, "missing ')' in macro parameter list")
         | 
| 674 | 
            +
                      end
         | 
| 675 | 
            +
                      if Token.is_keyword?(tok, K::ELLIPSIS)
         | 
| 676 | 
            +
                        param["__VA_ARGS__"] = make_macro_token(pos, true)
         | 
| 677 | 
            +
                        pos += 1
         | 
| 678 | 
            +
                        expect!(')')
         | 
| 679 | 
            +
                        return param, true
         | 
| 680 | 
            +
                      end
         | 
| 681 | 
            +
                      if tok.kind != T::IDENT
         | 
| 682 | 
            +
                        Util.errort!(tok, "identifier expected, but got #{tok}")
         | 
| 683 | 
            +
                      end
         | 
| 684 | 
            +
                      arg = tok.sval
         | 
| 685 | 
            +
                      if next?(K::ELLIPSIS)
         | 
| 686 | 
            +
                        expect!(')')
         | 
| 687 | 
            +
                        param[arg] = make_macro_token(pos, true)
         | 
| 688 | 
            +
                        pos += 1
         | 
| 689 | 
            +
                        return param, true
         | 
| 690 | 
            +
                      end
         | 
| 691 | 
            +
                      param[arg] = make_macro_token(pos, false)
         | 
| 692 | 
            +
                      pos += 1
         | 
| 693 | 
            +
                    end
         | 
| 694 | 
            +
                  end
         | 
| 695 | 
            +
             | 
| 696 | 
            +
                  # @param [Hash] param
         | 
| 697 | 
            +
                  # @return [<Token>]
         | 
| 698 | 
            +
                  def read_funclike_macro_body(param)
         | 
| 699 | 
            +
                    r = []
         | 
| 700 | 
            +
                    while true
         | 
| 701 | 
            +
                      tok = @impl.lex
         | 
| 702 | 
            +
                      if tok.kind == T::NEWLINE
         | 
| 703 | 
            +
                        return r
         | 
| 704 | 
            +
                      end
         | 
| 705 | 
            +
                      if tok.kind == T::IDENT
         | 
| 706 | 
            +
                        subst = param[tok.sval]
         | 
| 707 | 
            +
                        if subst
         | 
| 708 | 
            +
                          subst = copy_token(subst)
         | 
| 709 | 
            +
                          subst.space = tok.space
         | 
| 710 | 
            +
                          r.push(subst)
         | 
| 711 | 
            +
                          next
         | 
| 712 | 
            +
                        end
         | 
| 713 | 
            +
                      end
         | 
| 714 | 
            +
                      r.push(tok)
         | 
| 715 | 
            +
                    end
         | 
| 716 | 
            +
                  end
         | 
| 717 | 
            +
             | 
| 718 | 
            +
                  # @param [String] name
         | 
| 719 | 
            +
                  def read_obj_macro(name)
         | 
| 720 | 
            +
                    body = []
         | 
| 721 | 
            +
                    while true
         | 
| 722 | 
            +
                      tok = @impl.lex
         | 
| 723 | 
            +
                      if tok.kind == T::NEWLINE
         | 
| 724 | 
            +
                        break
         | 
| 725 | 
            +
                      end
         | 
| 726 | 
            +
                      body.push(tok)
         | 
| 727 | 
            +
                    end
         | 
| 728 | 
            +
                    hashhash_check!(body)
         | 
| 729 | 
            +
                    @macros[name] = make_obj_macro(body)
         | 
| 730 | 
            +
                  end
         | 
| 731 | 
            +
             | 
| 732 | 
            +
                  # @param [<Token>] v
         | 
| 733 | 
            +
                  # @raise
         | 
| 734 | 
            +
                  def hashhash_check!(v)
         | 
| 735 | 
            +
                    if v.size == 0
         | 
| 736 | 
            +
                      return
         | 
| 737 | 
            +
                    end
         | 
| 738 | 
            +
                    if Token.is_keyword?(v.first, K::HASHHASH)
         | 
| 739 | 
            +
                      Util.errort!(vec_head(v), "'##' cannot appear at start of macro expansion")
         | 
| 740 | 
            +
                    end
         | 
| 741 | 
            +
                    if Token.is_keyword?(v.last, K::HASHHASH)
         | 
| 742 | 
            +
                      Util.errort!(vec_tail(v), "'##' cannot appear at end of macro expansion")
         | 
| 743 | 
            +
                    end
         | 
| 744 | 
            +
                  end
         | 
| 745 | 
            +
             | 
| 746 | 
            +
                  # @return [Token]
         | 
| 747 | 
            +
                  def read_ident
         | 
| 748 | 
            +
                    tok = @impl.lex
         | 
| 749 | 
            +
                    if tok.kind != T::IDENT
         | 
| 750 | 
            +
                      Util.errort!(tok, "identifier expected, but got #{tok}")
         | 
| 751 | 
            +
                    end
         | 
| 752 | 
            +
                    tok
         | 
| 753 | 
            +
                  end
         | 
| 754 | 
            +
             | 
| 755 | 
            +
                  # @return [<Token>]
         | 
| 756 | 
            +
                  def read_intexpr_line
         | 
| 757 | 
            +
                    r = []
         | 
| 758 | 
            +
                    while true
         | 
| 759 | 
            +
                      tok = read_expand_newline
         | 
| 760 | 
            +
                      if tok.kind == T::NEWLINE
         | 
| 761 | 
            +
                        return r
         | 
| 762 | 
            +
                      end
         | 
| 763 | 
            +
                      if Token.is_ident?(tok, "defined")
         | 
| 764 | 
            +
                        r.push(read_defined_op)
         | 
| 765 | 
            +
                      elsif tok.kind == T::IDENT
         | 
| 766 | 
            +
                        # C11 6.10.1.4 says that remaining identifiers
         | 
| 767 | 
            +
                        # should be replaced with pp-number 0.
         | 
| 768 | 
            +
                        r.push(CPP_TOKEN_ZERO)
         | 
| 769 | 
            +
                      else
         | 
| 770 | 
            +
                        r.push(tok)
         | 
| 771 | 
            +
                      end
         | 
| 772 | 
            +
                    end
         | 
| 773 | 
            +
                  end
         | 
| 774 | 
            +
             | 
| 775 | 
            +
                  # @return [Token]
         | 
| 776 | 
            +
                  def read_defined_op
         | 
| 777 | 
            +
                    tok = @impl.lex
         | 
| 778 | 
            +
                    if Token.is_keyword?(tok, '(')
         | 
| 779 | 
            +
                      tok = @impl.lex
         | 
| 780 | 
            +
                      expect!(')')
         | 
| 781 | 
            +
                    end
         | 
| 782 | 
            +
                    if tok.kind != T::IDENT
         | 
| 783 | 
            +
                      Util.errort!(tok, "identifier expected, but got #{tok}")
         | 
| 784 | 
            +
                    end
         | 
| 785 | 
            +
                    @macros[tok.sval] ? CPP_TOKEN_ONE : CPP_TOKEN_ZERO
         | 
| 786 | 
            +
                  end
         | 
| 787 | 
            +
             | 
| 788 | 
            +
                  # @param [Token] hash
         | 
| 789 | 
            +
                  def read_elif(hash)
         | 
| 790 | 
            +
                    if @cond_incl_stack.size == 0
         | 
| 791 | 
            +
                      Util.errort!(hash, "stray #elif")
         | 
| 792 | 
            +
                    end
         | 
| 793 | 
            +
                    ci = @cond_incl_stack.last
         | 
| 794 | 
            +
                    if ci.ctx == CondInclCtx::ELSE
         | 
| 795 | 
            +
                      Util.errort!(hash, "#elif after #else")
         | 
| 796 | 
            +
                    end
         | 
| 797 | 
            +
                    ci.ctx = CondInclCtx::ELIF
         | 
| 798 | 
            +
                    ci.include_guard = nil
         | 
| 799 | 
            +
                    if ci.wastrue || !read_constexpr
         | 
| 800 | 
            +
                      @impl.skip_cond_incl!
         | 
| 801 | 
            +
                      return
         | 
| 802 | 
            +
                    end
         | 
| 803 | 
            +
                    ci.wastrue = true
         | 
| 804 | 
            +
                  end
         | 
| 805 | 
            +
             | 
| 806 | 
            +
                  # @param [Token] hash
         | 
| 807 | 
            +
                  def read_else(hash)
         | 
| 808 | 
            +
                    if @cond_incl_stack.size == 0
         | 
| 809 | 
            +
                      Util.errort!(hash, "stray #else")
         | 
| 810 | 
            +
                    end
         | 
| 811 | 
            +
                    ci = @cond_incl_stack.last
         | 
| 812 | 
            +
                    if ci.ctx == CondInclCtx::ELSE
         | 
| 813 | 
            +
                      Util.errort!(hash, "#else appears in #else")
         | 
| 814 | 
            +
                    end
         | 
| 815 | 
            +
                    expect_newline!
         | 
| 816 | 
            +
                    ci.ctx = CondInclCtx::ELSE
         | 
| 817 | 
            +
                    ci.include_guard = nil
         | 
| 818 | 
            +
                    if ci.wastrue
         | 
| 819 | 
            +
                      @impl.skip_cond_incl!
         | 
| 820 | 
            +
                    end
         | 
| 821 | 
            +
                  end
         | 
| 822 | 
            +
             | 
| 823 | 
            +
                  # @param [Token] hash
         | 
| 824 | 
            +
                  def read_endif(hash)
         | 
| 825 | 
            +
                    if @cond_incl_stack.size == 0
         | 
| 826 | 
            +
                      Util.errort!(hash, "stray #endif")
         | 
| 827 | 
            +
                    end
         | 
| 828 | 
            +
                    ci = @cond_incl_stack.pop
         | 
| 829 | 
            +
                    expect_newline!
         | 
| 830 | 
            +
             | 
| 831 | 
            +
                    # Detect an #ifndef and #endif pair that guards the entire
         | 
| 832 | 
            +
                    # header file. Remember the macro name guarding the file
         | 
| 833 | 
            +
                    # so that we can skip the file next time.
         | 
| 834 | 
            +
                    if !ci.include_guard || ci.file != hash.file
         | 
| 835 | 
            +
                      return
         | 
| 836 | 
            +
                    end
         | 
| 837 | 
            +
                    last = skip_newlines!
         | 
| 838 | 
            +
                    if ci.file != last.file
         | 
| 839 | 
            +
                      @include_guard[ci.file.name] = ci.include_guard
         | 
| 840 | 
            +
                    end
         | 
| 841 | 
            +
                  end
         | 
| 842 | 
            +
             | 
| 843 | 
            +
                  # Skips all newlines and returns the first non-newline token.
         | 
| 844 | 
            +
                  #
         | 
| 845 | 
            +
                  # @return [Token]
         | 
| 846 | 
            +
                  def skip_newlines!
         | 
| 847 | 
            +
                    tok = @impl.lex
         | 
| 848 | 
            +
                    while (tok.kind == T::NEWLINE)
         | 
| 849 | 
            +
                      tok = @impl.lex
         | 
| 850 | 
            +
                    end
         | 
| 851 | 
            +
                    @impl.unget_token(tok)
         | 
| 852 | 
            +
                    tok
         | 
| 853 | 
            +
                  end
         | 
| 854 | 
            +
             | 
| 855 | 
            +
                  # @param [Token] hash
         | 
| 856 | 
            +
                  def read_error(hash)
         | 
| 857 | 
            +
                    Util.errort!(hash, "#error: #{read_error_message}")
         | 
| 858 | 
            +
                  end
         | 
| 859 | 
            +
             | 
| 860 | 
            +
                  # @param [Token] hash
         | 
| 861 | 
            +
                  def read_warning(hash)
         | 
| 862 | 
            +
                    print "#warning: #{read_error_message}\n"
         | 
| 863 | 
            +
                    # warnt(hash, "#warning: %s", read_error_message());
         | 
| 864 | 
            +
                  end
         | 
| 865 | 
            +
             | 
| 866 | 
            +
                  # @return [String]
         | 
| 867 | 
            +
                  def read_error_message
         | 
| 868 | 
            +
                    b = ""
         | 
| 869 | 
            +
                    while true
         | 
| 870 | 
            +
                      tok = @impl.lex
         | 
| 871 | 
            +
                      if tok.kind == T::NEWLINE
         | 
| 872 | 
            +
                        return b
         | 
| 873 | 
            +
                      end
         | 
| 874 | 
            +
                      if (b.size != 0) && tok.space
         | 
| 875 | 
            +
                        b << ' '
         | 
| 876 | 
            +
                      end
         | 
| 877 | 
            +
                      b << "#{tok}"
         | 
| 878 | 
            +
                    end
         | 
| 879 | 
            +
                  end
         | 
| 880 | 
            +
             | 
| 881 | 
            +
                  def read_undef
         | 
| 882 | 
            +
                    name = read_ident
         | 
| 883 | 
            +
                    expect_newline!
         | 
| 884 | 
            +
                    @macros.delete(name.sval)
         | 
| 885 | 
            +
                  end
         | 
| 886 | 
            +
                end
         | 
| 887 | 
            +
              end
         | 
| 888 | 
            +
            end
         |