]> git.saurik.com Git - cycript.git/blob - Analyze.cpp
Allow C++ tagless type reference, in struct field.
[cycript.git] / Analyze.cpp
1 /* Cycript - The Truly Universal Scripting Language
2 * Copyright (C) 2009-2016 Jay Freeman (saurik)
3 */
4
5 /* GNU Affero General Public License, Version 3 {{{ */
6 /*
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20 /* }}} */
21
22 #include <cmath>
23 #include <cstring>
24 #include <iostream>
25 #include <map>
26 #include <sstream>
27 #include <string>
28
29 #include <clang-c/Index.h>
30
31 #include "Bridge.hpp"
32 #include "Functor.hpp"
33 #include "Replace.hpp"
34 #include "Syntax.hpp"
35
36 static CXChildVisitResult CYVisit(CXCursor cursor, CXCursor parent, CXClientData arg) {
37 (*reinterpret_cast<const Functor<void (CXCursor)> *>(arg))(cursor);
38 return CXChildVisit_Continue;
39 }
40
41 static unsigned CYForChild(CXCursor cursor, const Functor<void (CXCursor)> &visitor) {
42 return clang_visitChildren(cursor, &CYVisit, const_cast<void *>(static_cast<const void *>(&visitor)));
43 }
44
45 static bool CYOneChild(CXCursor cursor, const Functor<void (CXCursor)> &visitor) {
46 bool visited(false);
47 CYForChild(cursor, fun([&](CXCursor child) {
48 _assert(!visited);
49 visited = true;
50 visitor(child);
51 }));
52 return visited;
53 }
54
55 struct CYCXString {
56 CXString value_;
57
58 CYCXString(CXString value) :
59 value_(value)
60 {
61 }
62
63 CYCXString(CXCursor cursor) :
64 value_(clang_getCursorSpelling(cursor))
65 {
66 }
67
68 CYCXString(CXCursorKind kind) :
69 value_(clang_getCursorKindSpelling(kind))
70 {
71 }
72
73 CYCXString(CXFile file) :
74 value_(clang_getFileName(file))
75 {
76 }
77
78 CYCXString(CXTranslationUnit unit, CXToken token) :
79 value_(clang_getTokenSpelling(unit, token))
80 {
81 }
82
83 ~CYCXString() {
84 clang_disposeString(value_);
85 }
86
87 operator const char *() const {
88 return clang_getCString(value_);
89 }
90
91 const char *Pool(CYPool &pool) const {
92 return pool.strdup(*this);
93 }
94
95 bool operator ==(const char *rhs) const {
96 const char *lhs(*this);
97 return lhs == rhs || strcmp(lhs, rhs) == 0;
98 }
99 };
100
101 template <void (&clang_get_Location)(CXSourceLocation, CXFile *, unsigned *, unsigned *, unsigned *) = clang_getSpellingLocation>
102 struct CYCXPosition {
103 CXFile file_;
104 unsigned line_;
105 unsigned column_;
106 unsigned offset_;
107
108 CYCXPosition(CXSourceLocation location) {
109 clang_get_Location(location, &file_, &line_, &column_, &offset_);
110 }
111
112 CYCXPosition(CXTranslationUnit unit, CXToken token) :
113 CYCXPosition(clang_getTokenLocation(unit, token))
114 {
115 }
116
117 CXSourceLocation Get(CXTranslationUnit unit) const {
118 return clang_getLocation(unit, file_, line_, column_);
119 }
120 };
121
122 template <void (&clang_get_Location)(CXSourceLocation, CXFile *, unsigned *, unsigned *, unsigned *)>
123 std::ostream &operator <<(std::ostream &out, const CYCXPosition<clang_get_Location> &position) {
124 if (position.file_ != NULL)
125 out << "[" << CYCXString(position.file_) << "]:";
126 out << position.line_ << ":" << position.column_ << "@" << position.offset_;
127 return out;
128 }
129
130 struct CYKey {
131 unsigned priority_ = 0;
132
133 std::string code_;
134 unsigned flags_;
135 };
136
137 typedef std::map<std::string, CYKey> CYKeyMap;
138
139 struct CYChildBaton {
140 CXTranslationUnit unit;
141 CYKeyMap &keys;
142
143 CYChildBaton(CXTranslationUnit unit, CYKeyMap &keys) :
144 unit(unit),
145 keys(keys)
146 {
147 }
148 };
149
150 struct CYTokens {
151 private:
152 CXTranslationUnit unit_;
153 CXToken *tokens_;
154 unsigned count_;
155 unsigned valid_;
156
157 public:
158 CYTokens(CXTranslationUnit unit, CXSourceRange range) :
159 unit_(unit)
160 {
161 clang_tokenize(unit_, range, &tokens_, &count_);
162
163
164 // libclang's tokenizer is horribly broken and returns "extra" tokens.
165 // this code goes back through the tokens and filters for good ones :/
166
167 CYCXPosition<> end(clang_getRangeEnd(range));
168 CYCXString file(end.file_);
169
170 for (valid_ = 0; valid_ != count_; ++valid_) {
171 CYCXPosition<> position(unit, tokens_[valid_]);
172 _assert(CYCXString(position.file_) == file);
173 if (position.offset_ >= end.offset_)
174 break;
175 }
176 }
177
178 CYTokens(CXTranslationUnit unit, CXCursor cursor) :
179 CYTokens(unit, clang_getCursorExtent(cursor))
180 {
181 }
182
183 ~CYTokens() {
184 clang_disposeTokens(unit_, tokens_, count_);
185 }
186
187 operator CXToken *() const {
188 return tokens_;
189 }
190
191 size_t size() const {
192 return valid_;
193 }
194 };
195
196 static CYUTF8String CYCXPoolUTF8Range(CYPool &pool, CXSourceRange range) {
197 CYCXPosition<> start(clang_getRangeStart(range));
198 CYCXPosition<> end(clang_getRangeEnd(range));
199 CYCXString file(start.file_);
200 _assert(file == CYCXString(end.file_));
201
202 CYPool temp;
203 size_t size;
204 char *data(static_cast<char *>(CYPoolFile(temp, file, &size)));
205 _assert(start.offset_ <= size && end.offset_ <= size && start.offset_ <= end.offset_);
206
207 CYUTF8String code;
208 code.size = end.offset_ - start.offset_;
209 code.data = pool.strndup(data + start.offset_, code.size);
210 return code;
211 }
212
213 static CYExpression *CYTranslateExpression(CXTranslationUnit unit, CXCursor cursor) {
214 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
215 case CXCursor_CallExpr: {
216 CYExpression *function(NULL);
217 CYList<CYArgument> arguments;
218 CYForChild(cursor, fun([&](CXCursor child) {
219 CYExpression *expression(CYTranslateExpression(unit, child));
220 if (function == NULL)
221 function = expression;
222 else
223 arguments->*$C_(expression);
224 }));
225 return $C(function, arguments);
226 } break;
227
228 case CXCursor_DeclRefExpr: {
229 return $V(CYCXString(cursor).Pool($pool));
230 } break;
231
232 case CXCursor_IntegerLiteral: {
233 // libclang doesn't provide any reasonable way to do this
234 // note: clang_tokenize doesn't work if this is a macro
235 // the token range starts inside the macro but ends after it
236 // the tokenizer freaks out and either fails with 0 tokens
237 // or returns some massive number of tokens ending here :/
238
239 CYUTF8String token(CYCXPoolUTF8Range($pool, clang_getCursorExtent(cursor)));
240 double value(CYCastDouble(token));
241 if (std::isnan(value))
242 return $V(token.data);
243 return $ CYNumber(value);
244 } break;
245
246 case CXCursor_CStyleCastExpr:
247 // XXX: most of the time, this is a "NoOp" integer cast; but we should check it
248
249 case CXCursor_UnexposedExpr:
250 // there is a very high probability that this is actually an "ImplicitCastExpr"
251 // "Douglas Gregor" <dgregor@apple.com> err'd on the incorrect side of this one
252 // http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110926/046998.html
253
254 case CXCursor_ParenExpr: {
255 CYExpression *pass(NULL);
256 CYOneChild(cursor, fun([&](CXCursor child) {
257 pass = CYTranslateExpression(unit, child);
258 }));
259 return pass;
260 } break;
261
262 default:
263 //std::cerr << "E:" << CYCXString(kind) << std::endl;
264 _assert(false);
265 }
266 }
267
268 static CYStatement *CYTranslateStatement(CXTranslationUnit unit, CXCursor cursor) {
269 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
270 case CXCursor_ReturnStmt: {
271 CYExpression *value(NULL);
272 CYOneChild(cursor, fun([&](CXCursor child) {
273 value = CYTranslateExpression(unit, child);
274 }));
275 return $ CYReturn(value);
276 } break;
277
278 default:
279 //std::cerr << "S:" << CYCXString(kind) << std::endl;
280 _assert(false);
281 }
282 }
283
284 static CYStatement *CYTranslateBlock(CXTranslationUnit unit, CXCursor cursor) {
285 CYList<CYStatement> statements;
286 CYForChild(cursor, fun([&](CXCursor child) {
287 statements->*CYTranslateStatement(unit, child);
288 }));
289 return $ CYBlock(statements);
290 }
291
292 static CYTypedIdentifier *CYDecodeType(CXType type);
293 static void CYParseType(CXType type, CYTypedIdentifier *typed);
294
295 static CYTypedIdentifier *CYDecodeType(CXType type, const CYCXString &identifier) {
296 CYTypedIdentifier *typed(CYDecodeType(type));
297 typed->identifier_ = $ CYIdentifier(identifier.Pool($pool));
298 return typed;
299 }
300
301 static void CYParseStructure(CXCursor cursor, CYTypedIdentifier *typed) {
302 CYList<CYTypeStructField> fields;
303 CYForChild(cursor, fun([&](CXCursor child) {
304 if (clang_getCursorKind(child) == CXCursor_FieldDecl) {
305 CYTypedIdentifier *field(CYDecodeType(clang_getCursorType(child), child));
306 fields->*$ CYTypeStructField(field);
307 }
308 }));
309
310 typed->specifier_ = $ CYTypeStruct(NULL, $ CYStructTail(fields));
311 }
312
313 static void CYParseCursor(CXType type, CXCursor cursor, CYTypedIdentifier *typed) {
314 CYCXString spelling(cursor);
315
316 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
317 case CXCursor_EnumDecl:
318 if (spelling[0] != '\0')
319 // XXX: should we have a special enum keyword?
320 typed->specifier_ = $ CYTypeVariable($I(spelling.Pool($pool)));
321 else
322 // XXX: maybe replace with "enum : int" instead of "int"
323 CYParseType(clang_getEnumDeclIntegerType(cursor), typed);
324 break;
325
326 case CXCursor_StructDecl: {
327 if (spelling[0] != '\0')
328 typed->specifier_ = $ CYTypeReference($I(spelling.Pool($pool)));
329 else
330 CYParseStructure(cursor, typed);
331 } break;
332
333 case CXCursor_UnionDecl: {
334 _assert(false);
335 } break;
336
337 default:
338 std::cerr << "C:" << CYCXString(kind) << std::endl;
339 _assert(false);
340 break;
341 }
342 }
343
344 static CYTypedParameter *CYParseSignature(CXType type, CYTypedIdentifier *typed) {
345 CYParseType(clang_getResultType(type), typed);
346 CYList<CYTypedParameter> parameters;
347 for (int i(0), e(clang_getNumArgTypes(type)); i != e; ++i)
348 parameters->*$ CYTypedParameter(CYDecodeType(clang_getArgType(type, i)));
349 return parameters;
350 }
351
352 static void CYParseFunction(CXType type, CYTypedIdentifier *typed) {
353 typed = typed->Modify($ CYTypeFunctionWith(clang_isFunctionTypeVariadic(type), CYParseSignature(type, typed)));
354 }
355
356 static void CYParseType(CXType type, CYTypedIdentifier *typed) {
357 switch (CXTypeKind kind = type.kind) {
358 case CXType_Unexposed: {
359 CXType result(clang_getResultType(type));
360 if (result.kind == CXType_Invalid)
361 CYParseCursor(type, clang_getTypeDeclaration(type), typed);
362 else
363 // clang marks function pointers as Unexposed but still supports them
364 CYParseFunction(type, typed);
365 } break;
366
367 case CXType_Bool: typed->specifier_ = $ CYTypeVariable("bool"); break;
368 case CXType_Float: typed->specifier_ = $ CYTypeVariable("float"); break;
369 case CXType_Double: typed->specifier_ = $ CYTypeVariable("double"); break;
370
371 case CXType_Char_U: typed->specifier_ = $ CYTypeCharacter(CYTypeNeutral); break;
372 case CXType_Char_S: typed->specifier_ = $ CYTypeCharacter(CYTypeNeutral); break;
373 case CXType_SChar: typed->specifier_ = $ CYTypeCharacter(CYTypeSigned); break;
374 case CXType_UChar: typed->specifier_ = $ CYTypeCharacter(CYTypeUnsigned); break;
375
376 case CXType_Short: typed->specifier_ = $ CYTypeIntegral(CYTypeSigned, 0); break;
377 case CXType_UShort: typed->specifier_ = $ CYTypeIntegral(CYTypeUnsigned, 0); break;
378
379 case CXType_Int: typed->specifier_ = $ CYTypeIntegral(CYTypeSigned, 1); break;
380 case CXType_UInt: typed->specifier_ = $ CYTypeIntegral(CYTypeUnsigned, 1); break;
381
382 case CXType_Long: typed->specifier_ = $ CYTypeIntegral(CYTypeSigned, 2); break;
383 case CXType_ULong: typed->specifier_ = $ CYTypeIntegral(CYTypeUnsigned, 2); break;
384
385 case CXType_LongLong: typed->specifier_ = $ CYTypeIntegral(CYTypeSigned, 3); break;
386 case CXType_ULongLong: typed->specifier_ = $ CYTypeIntegral(CYTypeUnsigned, 3); break;
387
388 case CXType_Int128: typed->specifier_ = $ CYTypeInt128(CYTypeSigned); break;
389 case CXType_UInt128: typed->specifier_ = $ CYTypeInt128(CYTypeUnsigned); break;
390
391 case CXType_BlockPointer: {
392 CXType pointee(clang_getPointeeType(type));
393 _assert(!clang_isFunctionTypeVariadic(pointee));
394 typed = typed->Modify($ CYTypeBlockWith(CYParseSignature(pointee, typed)));
395 } break;
396
397 case CXType_ConstantArray:
398 CYParseType(clang_getArrayElementType(type), typed);
399 typed = typed->Modify($ CYTypeArrayOf($D(clang_getArraySize(type))));
400 break;
401
402 case CXType_Enum:
403 typed->specifier_ = $ CYTypeVariable($pool.strdup(CYCXString(clang_getTypeSpelling(type))));
404 break;
405
406 case CXType_FunctionProto:
407 CYParseFunction(type, typed);
408 break;
409
410 case CXType_IncompleteArray:
411 // XXX: I should support these :/
412 _assert(false);
413 break;
414
415 case CXType_ObjCClass:
416 typed->specifier_ = $ CYTypeVariable("Class");
417 break;
418
419 case CXType_ObjCId:
420 typed->specifier_ = $ CYTypeVariable("id");
421 break;
422
423 case CXType_ObjCInterface:
424 typed->specifier_ = $ CYTypeVariable($pool.strdup(CYCXString(clang_getTypeSpelling(type))));
425 break;
426
427 case CXType_ObjCObjectPointer: {
428 CXType pointee(clang_getPointeeType(type));
429 if (pointee.kind != CXType_Unexposed) {
430 CYParseType(pointee, typed);
431 typed = typed->Modify($ CYTypePointerTo());
432 } else
433 // Clang seems to have internal typedefs for id and Class that are awkward
434 _assert(false);
435 } break;
436
437 case CXType_ObjCSel:
438 typed->specifier_ = $ CYTypeVariable("SEL");
439 break;
440
441 case CXType_Pointer:
442 CYParseType(clang_getPointeeType(type), typed);
443 typed = typed->Modify($ CYTypePointerTo());
444 break;
445
446 case CXType_Record:
447 typed->specifier_ = $ CYTypeReference($I($pool.strdup(CYCXString(clang_getTypeSpelling(type)))));
448 break;
449
450 case CXType_Typedef:
451 // use the declaration in order to isolate the name of the typedef itself
452 typed->specifier_ = $ CYTypeVariable($pool.strdup(CYCXString(clang_getTypeDeclaration(type))));
453 break;
454
455 case CXType_Vector:
456 _assert(false);
457 break;
458
459 case CXType_Void:
460 typed->specifier_ = $ CYTypeVoid();
461 break;
462
463 default:
464 std::cerr << "T:" << CYCXString(clang_getTypeKindSpelling(kind)) << std::endl;
465 std::cerr << "_: " << CYCXString(clang_getTypeSpelling(type)) << std::endl;
466 _assert(false);
467 }
468
469 if (clang_isConstQualifiedType(type))
470 typed = typed->Modify($ CYTypeConstant());
471 }
472
473 static CYTypedIdentifier *CYDecodeType(CXType type) {
474 CYTypedIdentifier *typed($ CYTypedIdentifier(NULL));
475 CYParseType(type, typed);
476 return typed;
477 }
478
479 static CXChildVisitResult CYChildVisit(CXCursor cursor, CXCursor parent, CXClientData arg) {
480 CYChildBaton &baton(*static_cast<CYChildBaton *>(arg));
481 CXTranslationUnit &unit(baton.unit);
482
483 CYCXString spelling(cursor);
484 std::string name(spelling);
485 std::ostringstream value;
486 unsigned priority(2);
487 unsigned flags(CYBridgeHold);
488
489 /*CXSourceLocation location(clang_getCursorLocation(cursor));
490 CYCXPosition<> position(location);
491 std::cerr << spelling << " " << position << std::endl;*/
492
493 try { switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
494 case CXCursor_EnumConstantDecl: {
495 value << clang_getEnumConstantDeclValue(cursor);
496 } break;
497
498 case CXCursor_MacroDefinition: {
499 CXSourceRange range(clang_getCursorExtent(cursor));
500 CYTokens tokens(unit, range);
501 _assert(tokens.size() != 0);
502
503 CXCursor cursors[tokens.size()];
504 clang_annotateTokens(unit, tokens, tokens.size(), cursors);
505
506 CYLocalPool local;
507 CYList<CYFunctionParameter> parameters;
508 unsigned offset(1);
509
510 if (tokens.size() != 1) {
511 CYCXPosition<> start(clang_getRangeStart(range));
512 CYCXString first(unit, tokens[offset]);
513 if (first == "(") {
514 CYCXPosition<> paren(unit, tokens[offset]);
515 if (start.offset_ + strlen(spelling) == paren.offset_) {
516 for (;;) {
517 _assert(++offset != tokens.size());
518 CYCXString token(unit, tokens[offset]);
519 parameters->*$P($B($I(token.Pool($pool))));
520 _assert(++offset != tokens.size());
521 CYCXString comma(unit, tokens[offset]);
522 if (comma == ")")
523 break;
524 _assert(comma == ",");
525 }
526 ++offset;
527 }
528 }
529 }
530
531 std::ostringstream body;
532 for (unsigned i(offset); i != tokens.size(); ++i) {
533 CYCXString token(unit, tokens[i]);
534 if (i != offset)
535 body << " ";
536 body << token;
537 }
538
539 if (!parameters)
540 value << body.str();
541 else {
542 CYOptions options;
543 CYOutput out(*value.rdbuf(), options);
544 out << '(' << "function" << '(';
545 out << parameters;
546 out << ')' << '{';
547 out << "return" << ' ';
548 value << body.str();
549 out << ';' << '}' << ')';
550 }
551 } break;
552
553 case CXCursor_StructDecl: {
554 if (spelling[0] == '\0')
555 goto skip;
556 if (!clang_isCursorDefinition(cursor))
557 priority = 1;
558
559 CYLocalPool pool;
560
561 CYTypedIdentifier typed(NULL);
562 CYParseStructure(cursor, &typed);
563
564 CYOptions options;
565 CYOutput out(*value.rdbuf(), options);
566 CYTypeExpression(&typed).Output(out, CYNoBFC);
567
568 value << ".withName(\"" << name << "\")";
569 name += "$cy";
570 flags = CYBridgeType;
571 } break;
572
573 case CXCursor_TypedefDecl: {
574 CYLocalPool local;
575
576 CYTypedIdentifier *typed(CYDecodeType(clang_getTypedefDeclUnderlyingType(cursor)));
577 if (typed->specifier_ == NULL)
578 value << "(typedef " << CYCXString(clang_getTypeSpelling(clang_getTypedefDeclUnderlyingType(cursor))) << ")";
579 else {
580 CYOptions options;
581 CYOutput out(*value.rdbuf(), options);
582 CYTypeExpression(typed).Output(out, CYNoBFC);
583 }
584 } break;
585
586 case CXCursor_FunctionDecl:
587 case CXCursor_VarDecl: {
588 std::string label;
589
590 CYList<CYFunctionParameter> parameters;
591 CYStatement *code(NULL);
592
593 CYLocalPool local;
594
595 CYForChild(cursor, fun([&](CXCursor child) {
596 switch (CXCursorKind kind = clang_getCursorKind(child)) {
597 case CXCursor_AsmLabelAttr:
598 label = CYCXString(child);
599 break;
600
601 case CXCursor_CompoundStmt:
602 code = CYTranslateBlock(unit, child);
603 break;
604
605 case CXCursor_ParmDecl:
606 parameters->*$P($B($I(CYCXString(child).Pool($pool))));
607 break;
608
609 case CXCursor_IntegerLiteral:
610 case CXCursor_ObjCClassRef:
611 case CXCursor_TypeRef:
612 case CXCursor_UnexposedAttr:
613 break;
614
615 default:
616 //std::cerr << "A:" << CYCXString(child) << std::endl;
617 break;
618 }
619 }));
620
621 if (label.empty()) {
622 label = spelling;
623 label = '_' + label;
624 } else if (label[0] != '_')
625 goto skip;
626
627 if (code == NULL) {
628 CXType type(clang_getCursorType(cursor));
629 value << "*(typedef " << CYCXString(clang_getTypeSpelling(type)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label.substr(1) << "'))";
630 } else {
631 CYOptions options;
632 CYOutput out(*value.rdbuf(), options);
633 CYFunctionExpression *function($ CYFunctionExpression(NULL, parameters, code));
634 function->Output(out, CYNoBFC);
635 //std::cerr << value.str() << std::endl;
636 }
637 } break;
638
639 default: {
640 return CXChildVisit_Recurse;
641 } break;
642 } {
643 CYKey &key(baton.keys[name]);
644 if (key.priority_ <= priority) {
645 key.priority_ = priority;
646 key.code_ = value.str();
647 key.flags_ = flags;
648 }
649 } } catch (const CYException &error) {
650 CYPool pool;
651 //std::cerr << error.PoolCString(pool) << std::endl;
652 }
653
654 skip:
655 return CXChildVisit_Continue;
656 }
657
658 int main(int argc, const char *argv[]) {
659 CXIndex index(clang_createIndex(0, 0));
660
661 const char *file(argv[1]);
662
663 unsigned offset(3);
664 #if CY_OBJECTIVEC
665 argv[--offset] = "-ObjC++";
666 #endif
667
668 CXTranslationUnit unit(clang_parseTranslationUnit(index, file, argv + offset, argc - offset, NULL, 0, CXTranslationUnit_DetailedPreprocessingRecord));
669
670 for (unsigned i(0), e(clang_getNumDiagnostics(unit)); i != e; ++i) {
671 CXDiagnostic diagnostic(clang_getDiagnostic(unit, i));
672 CYCXString spelling(clang_getDiagnosticSpelling(diagnostic));
673 std::cerr << spelling << std::endl;
674 }
675
676 CYKeyMap keys;
677 CYChildBaton baton(unit, keys);
678 clang_visitChildren(clang_getTranslationUnitCursor(unit), &CYChildVisit, &baton);
679
680 for (CYKeyMap::const_iterator key(keys.begin()); key != keys.end(); ++key) {
681 std::string code(key->second.code_);
682 for (size_t i(0), e(code.size()); i != e; ++i)
683 if (code[i] <= 0 || code[i] >= 0x7f || code[i] == '\n')
684 goto skip;
685 std::cout << key->first << "|" << key->second.flags_ << "\"" << code << "\"" << std::endl;
686 skip:; }
687
688 clang_disposeTranslationUnit(unit);
689 clang_disposeIndex(index);
690
691 return 0;
692 }