Merge pull request #4141 from udzura/add-test-for-attr-nil-guard
[mruby.git] / lib / mruby / presym.rb
blob016c2b20e7be72dd476d531378695c8392a0078f
1 module MRuby
2   class Presym
3     OPERATORS = {
4       "!" => "not",
5       "%" => "mod",
6       "&" => "and",
7       "*" => "mul",
8       "+" => "add",
9       "-" => "sub",
10       "/" => "div",
11       "<" => "lt",
12       ">" => "gt",
13       "^" => "xor",
14       "`" => "tick",
15       "|" => "or",
16       "~" => "neg",
17       "!=" => "neq",
18       "!~" => "nmatch",
19       "&&" => "andand",
20       "**" => "pow",
21       "+@" => "plus",
22       "-@" => "minus",
23       "<<" => "lshift",
24       "<=" => "le",
25       "==" => "eq",
26       "=~" => "match",
27       ">=" => "ge",
28       ">>" => "rshift",
29       "[]" => "aref",
30       "||" => "oror",
31       "<=>" => "cmp",
32       "===" => "eqq",
33       "[]=" => "aset",
34     }.freeze
36     SYMBOL_TO_MACRO = {
37     #      Symbol      =>      Macro
38     # [prefix, suffix] => [prefix, suffix]
39       ["@@"  , ""    ] => ["CV"  , ""    ],
40       ["@"   , ""    ] => ["IV"  , ""    ],
41       [""    , "!"   ] => [""    , "_B"  ],
42       [""    , "?"   ] => [""    , "_Q"  ],
43       [""    , "="   ] => [""    , "_E"  ],
44       [""    , ""    ] => [""    , ""    ],
45     }.freeze
47     C_STR_LITERAL_RE = /"(?:[^\\\"]|\\.)*"/
49     def initialize(build)
50       @build = build
51     end
53     def scan(paths)
54       presym_hash = {}
55       paths.each {|path| read_preprocessed(presym_hash, path)}
56       presym_hash.keys.sort_by!{|sym| [c_literal_size(sym), sym]}
57     end
59     def read_list
60       File.readlines(list_path, mode: "r:binary").each(&:chomp!)
61     end
63     def write_list(presyms)
64       _pp "GEN", list_path.relative_path
65       File.binwrite(list_path, presyms.join("\n") << "\n")
66     end
68     def write_id_header(presyms)
69       prefix_re = Regexp.union(*SYMBOL_TO_MACRO.keys.map(&:first).uniq)
70       suffix_re = Regexp.union(*SYMBOL_TO_MACRO.keys.map(&:last).uniq)
71       sym_re = /\A(#{prefix_re})?([\w&&\D]\w*)(#{suffix_re})?\z/o
72       _pp "GEN", id_header_path.relative_path
73       File.open(id_header_path, "w:binary") do |f|
74         f.puts "enum mruby_presym {"
75         presyms.each.with_index(1) do |sym, num|
76           if sym_re =~ sym && (affixes = SYMBOL_TO_MACRO[[$1, $3]])
77             f.puts "  MRB_#{affixes * 'SYM'}__#{$2} = #{num},"
78           elsif name = OPERATORS[sym]
79             f.puts "  MRB_OPSYM__#{name} = #{num},"
80           end
81         end
82         f.puts "};"
83         f.puts
84         f.puts "#define MRB_PRESYM_MAX #{presyms.size}"
85       end
86     end
88     def write_table_header(presyms)
89       _pp "GEN", table_header_path.relative_path
90       File.open(table_header_path, "w:binary") do |f|
91         f.puts "static const uint16_t presym_length_table[] = {"
92         presyms.each{|sym| f.puts "  #{sym.bytesize},\t/* #{sym} */"}
93         f.puts "};"
94         f.puts
95         f.puts "static const char * const presym_name_table[] = {"
96         presyms.each{|sym| f.puts %|  "#{sym}",|}
97         f.puts "};"
98       end
99     end
101     def list_path
102       @list_path ||= "#{@build.build_dir}/presym".freeze
103     end
105     def header_dir;
106       @header_dir ||= "#{@build.build_dir}/include/mruby/presym".freeze
107     end
109     def id_header_path
110       @id_header_path ||= "#{header_dir}/id.h".freeze
111     end
113     def table_header_path
114       @table_header_path ||= "#{header_dir}/table.h".freeze
115     end
117     private
119     def read_preprocessed(presym_hash, path)
120       File.binread(path).scan(/<@! (.*?) !@>/) do |part,|
121         literals = part.scan(C_STR_LITERAL_RE)
122         presym_hash[literals.map{|l| l[1..-2]}.join] = true unless literals.empty?
123       end
124     end
126     def c_literal_size(literal_without_quote)
127       literal_without_quote.size  # TODO: consider escape sequence
128     end
129   end