class AdLint::Cpp::FunctionLikeMacro
Attributes
parameter_names[R]
Public Class Methods
new(define_line)
click to toggle source
Calls superclass method
AdLint::Cpp::Macro::new
# File lib/adlint/cpp/macro.rb, line 100 def initialize(define_line) super if params = define_line.identifier_list @parameter_names = params.identifiers.map { |tok| tok.value } else @parameter_names = [] end end
Public Instance Methods
expand(toks, macro_tbl, repl_ctxt)
click to toggle source
Calls superclass method
AdLint::Cpp::Macro#expand
# File lib/adlint/cpp/macro.rb, line 124 def expand(toks, macro_tbl, repl_ctxt) super args, * = parse_arguments(toks, 1) args = [] if @parameter_names.empty? args_hash = @parameter_names.zip(args).each_with_object({}) { |(param, arg), hash| hash[param] = arg } rslt_toks = expand_replacement_list(args_hash, toks.first.location, macro_tbl, repl_ctxt) macro_tbl.notify_function_like_macro_replacement(self, toks, args, rslt_toks) rslt_toks end
function_like?()
click to toggle source
# File lib/adlint/cpp/macro.rb, line 141 def function_like?; true end
replaceable_size(toks)
click to toggle source
# File lib/adlint/cpp/macro.rb, line 111 def replaceable_size(toks) if name.value == toks.first.value args, idx = parse_arguments(toks, 1) case when args && @parameter_names.empty? return idx + 1 when args && @parameter_names.size >= args.size return idx + 1 end end 0 end
Private Instance Methods
concat_with_last_token(arg_toks, expansion_loc, rslt_toks, macro_tbl)
click to toggle source
# File lib/adlint/cpp/macro.rb, line 313 def concat_with_last_token(arg_toks, expansion_loc, rslt_toks, macro_tbl) # NOTE: The ISO C99 standard says; # # 6.10.3.3 The ## operator # # Constraints # # 1 A ## preprocessing token shall not occur at the beginning or at the # end of a replacement list for either form of macro definition. # # Semantics # # 2 If, in the replacement list of a function-form macro, a parameter is # immediately preceded or followed by a ## preprocessing token, the # parameter is replaced by the corresponding argument's preprocessing # token sequence; however, if an argument consists of no preprocessing # tokens, the parameter is replaced by a placemarker preprocessing # token instead. # # 3 For both object-like and function-like macro invocations, before the # replacement list is reexamined for more macro names to replace, each # instance of a ## preprocessing token in the replacement list (not # from an argument) is deleted and the preceding preprocessing token is # concatenated with the following preprocessing token. Placemarker # preprocessing tokens are handled specially: concatenation of two # placemarkers results in a single placemarker preprocessing token, and # concatenation of a placemarker with a non-placemarker preprocessing # token results in the non-placemarker preprocessing token. If the # result is not a valid preprocessing token, the behavior is undefined. # The resulting token is available for further macro replacement. The # order of evaluation of ## operators is unspecified. if lhs = rslt_toks.pop unless arg_toks.empty? # NOTE: To avoid syntax error when the concatenated token can be # retokenize to two or more tokens. unlexed_arg = unlex_tokens(arg_toks) new_toks = StringToPPTokensLexer.new(lhs.value + unlexed_arg).execute new_toks.map! do |tok| ReplacedToken.new(tok.type, tok.value, expansion_loc, tok.type_hint, false) end rslt_toks.concat(new_toks) macro_tbl.notify_sharpsharp_operator_evaled( lhs, Token.new(:MACRO_ARG, unlexed_arg, arg_toks.first.location), new_toks) else new_toks = [ReplacedToken.new(lhs.type, lhs.value, expansion_loc, lhs.type_hint, false)] rslt_toks.concat(new_toks) end end end
expand_replacement_list(args, loc, macro_tbl, repl_ctxt)
click to toggle source
# File lib/adlint/cpp/macro.rb, line 198 def expand_replacement_list(args, loc, macro_tbl, repl_ctxt) unless repl_list = self.replacement_list return [] end rslt_toks = [] idx = 0 while cur_tok = repl_list.tokens[idx] nxt_tok = repl_list.tokens[idx + 1] case when arg = args[cur_tok.value] substitute_argument(cur_tok, nxt_tok, arg, loc, rslt_toks, macro_tbl, repl_ctxt) when cur_tok.value == "#" if nxt_tok tok = stringize_argument(args[nxt_tok.value], loc, macro_tbl) rslt_toks.push(tok) idx += 1 end when cur_tok.value == "##" && nxt_tok.value == "#" if nxt_nxt_tok = repl_list.tokens[idx + 2] tok = stringize_argument(args[nxt_nxt_tok.value], loc, macro_tbl) concat_with_last_token([tok], loc, rslt_toks, macro_tbl) idx += 2 end when cur_tok.value == "##" if nxt_tok and arg = args[nxt_tok.value] concat_with_last_token(arg, loc, rslt_toks, macro_tbl) else concat_with_last_token([nxt_tok], loc, rslt_toks, macro_tbl) end idx += 1 else rslt_toks.push(ReplacedToken.new(cur_tok.type, cur_tok.value, loc, cur_tok.type_hint, false)) end idx += 1 end rslt_toks end
parse_arguments(toks, idx)
click to toggle source
# File lib/adlint/cpp/macro.rb, line 144 def parse_arguments(toks, idx) while tok = toks[idx] case when tok.type == :NEW_LINE idx += 1 when tok.value == "(" idx += 1 break else return nil, idx end end return nil, idx unless tok args = [] loop do arg, idx, lst = parse_one_argument(toks, idx) args.push(arg) break if lst end return args, idx end
parse_one_argument(toks, idx)
click to toggle source
# File lib/adlint/cpp/macro.rb, line 167 def parse_one_argument(toks, idx) arg = [] paren_depth = 0 while tok = toks[idx] case tok.value when "(" arg.push(tok) paren_depth += 1 when ")" paren_depth -= 1 if paren_depth >= 0 arg.push(tok) else return arg, idx, true end when "," if paren_depth > 0 arg.push(tok) else return arg, idx + 1, false end when "\n" ; else arg.push(tok) end idx += 1 end return arg, idx, true # NOTREACHED end
stringize_argument(arg, expansion_loc, macro_tbl)
click to toggle source
# File lib/adlint/cpp/macro.rb, line 267 def stringize_argument(arg, expansion_loc, macro_tbl) # NOTE: The ISO C99 standard says; # # 6.10.3.2 The # operator # # Constraints # # 1 Each # preprocessing token in the replacement list for a # function-like macro shall be followed by a parameter as the next # preprocessing token in the replacement list. # # Semantics # # 2 If, in the replacement list, a parameter is immediately proceeded by # a # preprocessing token, both are replaced by a single character # string literal preprocessing token that contains the spelling of the # preprocessing token sequence for the corresponding argument. Each # occurrence of white space between the argument's preprocessing tokens # becomes a single space character in the character string literal. # White space before the first preprocessing token and after the last # preprocessing token composing the argument is deleted. Otherwise, # the original spelling of each preprocessing token in the argument is # retained in the character string literal, except for special handling # for producing the spelling of string literals and character # constants: a \ character is inserted before each " and \ character of # a character constant or string literal (including the delimiting " # characters), except that it is implementation-defined whether a \ # character is inserted before the \ character beginning of universal # character name. If the replacement that results is not a valid # character string literal, the behavior is undefined. The character # string literal corresponding to an empty argument is "". The order # of evaluation of # and ## operators is unspecified. # # NOTE: This code does not concern about contents of the string literal. # But, it is enough for analysis. str = arg.map { |tok| tok.value }.join if str =~ /((:?\\*))\\\z/ && $1.length.even? str.chop! macro_tbl.notify_last_backslash_ignored(arg.last) end ReplacedToken.new(:PP_TOKEN, "\"#{str.gsub(/["\\]/) { "\\" + $& }}\"", expansion_loc, :STRING_LITERAL, false) end
substitute_argument(param_tok, nxt_tok, arg, loc, rslt_toks, macro_tbl, repl_ctxt)
click to toggle source
# File lib/adlint/cpp/macro.rb, line 240 def substitute_argument(param_tok, nxt_tok, arg, loc, rslt_toks, macro_tbl, repl_ctxt) # NOTE: The ISO C99 standard says; # # 6.10.3.1 Argument substitution # # 1 After the arguments for the invocation of a function-like macro have # been identified, argument substitution take place. A parameter in # the replacement list, unless proceeded by a # or ## preprocessing # token or followed by a ## preprocessing token, is replaced by the # corresponding argument after all macros contained therein have been # expanded. Before being substituted, each argument's preprocessing # tokens are completely macro replaced as if they formed the rest of # the preprocessing file; no other preprocessing tokens are available. if nxt_tok && nxt_tok.value == "##" rslt_toks.concat(arg.map { |tok| ReplacedToken.new(tok.type, tok.value, loc, tok.type_hint, false) }) else macro_tbl.replace(arg, repl_ctxt) rslt_toks.concat(arg.map { |tok| ReplacedToken.new(tok.type, tok.value, loc, tok.type_hint, true) }) end end
unlex_tokens(arg_toks)
click to toggle source
# File lib/adlint/cpp/macro.rb, line 368 def unlex_tokens(arg_toks) # NOTE: To regenerate source string from pp_tokens in preparation for # the argument substitution. # 6.10.3 Macro replacement # # Semantics # # 11 The sequence of preprocessing tokens bounded by the outside-most # matching parentheses forms the list of arguments for the # function-like macro. The individual arguments within the list are # separated by comma preprocessing tokens, but comma preprocessing # tokens between matching inner parentheses do not separate arguments. # If there are sequences of preprocessing tokens within the list of # arguments that would otherwise act as preprocessing directives, the # behavior is undefined. arg_toks.each_cons(2).reduce(arg_toks.first.value) do |str, (lhs, rhs)| lhs_loc, rhs_loc = lhs.location, rhs.location if lhs_loc.line_no == rhs_loc.line_no tok_gap = rhs_loc.column_no - lhs_loc.column_no - lhs.value.length str + " " * [tok_gap, 0].max + rhs.value else "#{str} #{rhs.value}" end end end