vxx2 / vlib / v3 / gen / c / names.v
234 lines · 226 sloc · 4.92 KB · 288feee4702b35beadb4037d02b96b5c8674156b
Raw
1module c
2
3import strings
4
5// c_reserved_words is a set (not a list) so `name in c_reserved_words` is an O(1) hash
6// lookup. c_name() runs on every emitted identifier, so a linear scan here is costly.
7const c_reserved_words = {
8 'auto': true
9 'break': true
10 'case': true
11 'char': true
12 'const': true
13 'continue': true
14 'copy': true
15 'default': true
16 'do': true
17 'double': true
18 'else': true
19 'enum': true
20 'extern': true
21 'float': true
22 'for': true
23 'goto': true
24 'if': true
25 'inline': true
26 'int': true
27 'long': true
28 'register': true
29 'restrict': true
30 'return': true
31 'short': true
32 'signed': true
33 'sizeof': true
34 'static': true
35 'struct': true
36 'switch': true
37 'typedef': true
38 'union': true
39 'unsigned': true
40 'void': true
41 'volatile': true
42 'while': true
43 'unix': true
44}
45
46// c_libc_collisions are libc function names that are not C keywords but clash at
47// link/declaration time when a user defines a plain (module `main`) function with
48// the same name (e.g. `fn rint(...)` vs libc's `double rint(double)`). They are
49// mangled to `v_<name>` consistently at definition and call sites. `C.<name>`
50// calls are unaffected (the `C.` prefix is stripped before this check).
51const c_libc_collisions = {
52 'rint': true
53 'y0': true
54 'y1': true
55 'yn': true
56 'j0': true
57 'j1': true
58 'jn': true
59 'drem': true
60 'scalb': true
61}
62
63// c_name converts c name data for c.
64fn c_name(name string) string {
65 if name.starts_with('C.') {
66 return name[2..]
67 }
68 if name == 'malloc' {
69 return 'v_malloc'
70 }
71 if name == 'int_str' {
72 return 'int__str'
73 }
74 // The V builtin `exit` wraps `C.exit`; both lower to the C symbol `exit`.
75 // Rename the V function (and its call sites) to `v_exit` so its body's
76 // `C.exit(code)` call resolves to libc `exit` instead of recursing forever.
77 // `C.exit` itself is handled by the `C.` strip above, so it stays `exit`.
78 if name == 'exit' {
79 return 'v_exit'
80 }
81 if c_name_is_plain(name) {
82 if name in c_reserved_words || name in c_libc_collisions {
83 return 'v_${name}'
84 }
85 return name
86 }
87 n := c_name_sanitize(name)
88 if n in c_reserved_words || n in c_libc_collisions {
89 return 'v_${n}'
90 }
91 return n
92}
93
94// c_name_sanitize converts c name sanitize data for c.
95fn c_name_sanitize(name string) string {
96 mut b := strings.new_builder(name.len + 8)
97 mut i := 0
98 for i < name.len {
99 c := name[i]
100 if c == `[` {
101 if i + 1 < name.len && name[i + 1] == `]` {
102 b.write_string('Array_')
103 i += 2
104 continue
105 }
106 b.write_u8(`_`)
107 } else if c == `]` {
108 i++
109 continue
110 } else if c == `.` {
111 if i + 1 < name.len {
112 next := name[i + 1]
113 if next == `-` {
114 b.write_string('__minus')
115 i += 2
116 continue
117 }
118 if next == `+` {
119 b.write_string('__plus')
120 i += 2
121 continue
122 }
123 if next == `*` {
124 b.write_string('__mul')
125 i += 2
126 continue
127 }
128 if next == `/` {
129 b.write_string('__div')
130 i += 2
131 continue
132 }
133 if next == `%` {
134 b.write_string('__mod')
135 i += 2
136 continue
137 }
138 if next == `&` {
139 b.write_string('__and')
140 i += 2
141 continue
142 }
143 if next == `|` {
144 b.write_string('__or')
145 i += 2
146 continue
147 }
148 if next == `^` {
149 b.write_string('__xor')
150 i += 2
151 continue
152 }
153 if i + 2 < name.len {
154 op := name[i + 2]
155 if next == `=` && op == `=` {
156 b.write_string('__eq')
157 i += 3
158 continue
159 }
160 if next == `!` && op == `=` {
161 b.write_string('__ne')
162 i += 3
163 continue
164 }
165 if next == `<` && op == `=` {
166 b.write_string('__le')
167 i += 3
168 continue
169 }
170 if next == `>` && op == `=` {
171 b.write_string('__ge')
172 i += 3
173 continue
174 }
175 if next == `<` && op == `<` {
176 b.write_string('__left_shift')
177 i += 3
178 continue
179 }
180 if next == `>` && op == `>` {
181 b.write_string('__right_shift')
182 i += 3
183 continue
184 }
185 }
186 if next == `<` {
187 b.write_string('__lt')
188 i += 2
189 continue
190 }
191 if next == `>` {
192 b.write_string('__gt')
193 i += 2
194 continue
195 }
196 }
197 b.write_string('__')
198 } else if c == `&` {
199 b.write_string('ptr')
200 } else if c == `@` {
201 i++
202 continue
203 } else if c == `,` || c == ` ` {
204 b.write_u8(`_`)
205 } else {
206 b.write_u8(c)
207 }
208 i++
209 }
210 return b.str()
211}
212
213// c_name_is_plain converts c name is plain data for c.
214fn c_name_is_plain(name string) bool {
215 for i in 0 .. name.len {
216 c := name[i]
217 if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || (c >= `0` && c <= `9`) || c == `_` {
218 continue
219 }
220 return false
221 }
222 return true
223}
224
225fn c_local_name(name string) string {
226 local_name := if name.contains('.') { name.all_after_last('.') } else { name }
227 return c_name(local_name)
228}
229
230// c_escape supports c escape handling for c.
231fn c_escape(s string) string {
232 return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\t', '\\t').replace('\r',
233 '\\r')
234}
235