1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
# frozen_string_literal: true
require "ripper"
module TestProf
module RSpecStamp
module Parser
class Ripper
def parse(code)
sexp = ::Ripper.sexp(code)
return unless sexp
# sexp has the following format:
# [:program,
# [
# [
# :command,
# [:@ident, "it", [1, 0]],
# [:args_add_block, [ ... ]]
# ]
# ]
# ]
#
# or
#
# [:program,
# [
# [
# :vcall,
# [:@ident, "it", [1, 0]]
# ]
# ]
# ]
res = Result.new
fcall = sexp[1][0][1]
args_block = sexp[1][0][2]
if fcall.first == :fcall
fcall = fcall[1]
elsif fcall.first == :var_ref
res.fname = [parse_const(fcall), sexp[1][0][3][1]].join(".")
args_block = sexp[1][0][4]
end
res.fname ||= fcall[1]
return res if args_block.nil?
args_block = args_block[1] if args_block.first == :arg_paren
args = args_block[1]
if args.first.first == :string_literal
res.desc = parse_literal(args.shift)
elsif args.first.first == :var_ref || args.first.first == :const_path_ref
res.desc_const = parse_const(args.shift)
end
parse_arg(res, args.shift) until args.empty?
res
end
private
def parse_arg(res, arg)
if arg.first == :symbol_literal
res.add_tag parse_literal(arg)
elsif arg.first == :bare_assoc_hash
parse_hash(res, arg[1])
end
end
def parse_hash(res, hash_arg)
hash_arg.each do |(_, label, val)|
res.add_htag label[1][0..-2].to_sym, parse_value(val)
end
end
# Expr of the form:
# bool - [:var_ref, [:@kw, "true", [1, 24]]]
# string - [:string_literal, [:string_content, [...]]]
# int - [:@int, "3", [1, 52]]]]
def parse_value(expr)
case expr.first
when :var_ref
expr[1][1] == "true"
when :@int
expr[1].to_i
when :@float
expr[1].to_f
else
parse_literal(expr)
end
end
# Expr of the form:
# [:string_literal, [:string_content, [:@tstring_content, "is", [1, 4]]]]
def parse_literal(expr)
val = expr[1][1][1]
val = val.to_sym if expr[0] == :symbol_literal ||
expr[0] == :assoc_new
val
end
# Expr of the form:
# [:var_ref, [:@const, "User", [1, 9]]]
#
# or
#
# [:const_path_ref, [:const_path_ref, [:var_ref,
# [:@const, "User", [1, 17]]],
# [:@const, "Guest", [1, 23]]],
# [:@const, "Collection", [1, 30]]
def parse_const(expr)
if expr.first == :var_ref
expr[1][1]
elsif expr.first == :@const
expr[1]
elsif expr.first == :const_path_ref
expr[1..].map(&method(:parse_const)).join("::")
end
end
end
end
end
end
|