| [3b3491b6] | 1 | // | 
|---|
|  | 2 | // Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo | 
|---|
|  | 3 | // | 
|---|
|  | 4 | // The contents of this file are covered under the licence agreement in the | 
|---|
|  | 5 | // file "LICENCE" distributed with Cforall. | 
|---|
|  | 6 | // | 
|---|
|  | 7 | // ResolvProtoDump.cc -- Translates CFA resolver instances into resolv-proto instances | 
|---|
|  | 8 | // | 
|---|
|  | 9 | // Author           : Aaron Moss | 
|---|
|  | 10 | // Created On       : Tue Sep 11 09:04:00 2018 | 
|---|
| [60a8062] | 11 | // Last Modified By : Peter A. Buhr | 
|---|
|  | 12 | // Last Modified On : Sat Feb 15 13:50:11 2020 | 
|---|
|  | 13 | // Update Count     : 3 | 
|---|
| [3b3491b6] | 14 | // | 
|---|
|  | 15 |  | 
|---|
|  | 16 | #include <algorithm> | 
|---|
|  | 17 | #include <cctype> | 
|---|
|  | 18 | #include <iostream> | 
|---|
|  | 19 | #include <memory> | 
|---|
|  | 20 | #include <list> | 
|---|
|  | 21 | #include <set> | 
|---|
|  | 22 | #include <sstream> | 
|---|
|  | 23 | #include <string> | 
|---|
|  | 24 | #include <unordered_set> | 
|---|
| [40cd873] | 25 | #include <utility> | 
|---|
| [3b3491b6] | 26 | #include <vector> | 
|---|
|  | 27 |  | 
|---|
|  | 28 | #include "Common/PassVisitor.h" | 
|---|
| [4d59ff9] | 29 | #include "Common/utility.h" | 
|---|
| [3b3491b6] | 30 | #include "CodeGen/OperatorTable.h" | 
|---|
|  | 31 | #include "SynTree/Declaration.h" | 
|---|
|  | 32 | #include "SynTree/Expression.h" | 
|---|
| [4d59ff9] | 33 | #include "SynTree/Initializer.h" | 
|---|
| [3b3491b6] | 34 | #include "SynTree/Statement.h" | 
|---|
|  | 35 | #include "SynTree/Type.h" | 
|---|
|  | 36 |  | 
|---|
|  | 37 | namespace CodeTools { | 
|---|
|  | 38 |  | 
|---|
|  | 39 | /// Visitor for dumping resolver prototype output | 
|---|
|  | 40 | class ProtoDump : public WithShortCircuiting, public WithVisitorRef<ProtoDump> { | 
|---|
| [a4a000d] | 41 | std::set<std::string> decls;             ///< Declarations in this scope | 
|---|
|  | 42 | std::vector<std::string> exprs;          ///< Expressions in this scope | 
|---|
|  | 43 | std::vector<ProtoDump> subs;             ///< Sub-scopes | 
|---|
|  | 44 | std::unordered_set<std::string> closed;  ///< Closed type variables | 
|---|
|  | 45 | const ProtoDump* parent;                 ///< Outer lexical scope | 
|---|
|  | 46 | std::unique_ptr<Type> rtnType;           ///< Return type for this scope | 
|---|
| [3b3491b6] | 47 |  | 
|---|
|  | 48 | public: | 
|---|
|  | 49 | /// Default constructor for root ProtoDump | 
|---|
| [a4a000d] | 50 | ProtoDump() : decls(), exprs(), subs(), closed(), parent(nullptr), rtnType(nullptr) {} | 
|---|
| [3b3491b6] | 51 |  | 
|---|
|  | 52 | /// Child constructor | 
|---|
| [a2dbcff1] | 53 | ProtoDump(const ProtoDump* p, Type* r) | 
|---|
| [a4a000d] | 54 | : decls(), exprs(), subs(), closed(p->closed), parent(p), rtnType(r) {} | 
|---|
| [4d59ff9] | 55 |  | 
|---|
|  | 56 | // Fix copy issues | 
|---|
| [a2dbcff1] | 57 | ProtoDump(const ProtoDump& o) | 
|---|
|  | 58 | : decls(o.decls), exprs(o.exprs), subs(o.subs), closed(o.closed), parent(o.parent), | 
|---|
| [4d59ff9] | 59 | rtnType(maybeClone(o.rtnType.get())) {} | 
|---|
|  | 60 | ProtoDump( ProtoDump&& ) = default; | 
|---|
|  | 61 |  | 
|---|
| [a4a000d] | 62 | ProtoDump& operator= (const ProtoDump& o) { | 
|---|
| [4d59ff9] | 63 | if ( this == &o ) return *this; | 
|---|
|  | 64 |  | 
|---|
|  | 65 | decls = o.decls; | 
|---|
|  | 66 | exprs = o.exprs; | 
|---|
|  | 67 | subs = o.subs; | 
|---|
| [a4a000d] | 68 | closed = o.closed; | 
|---|
| [4d59ff9] | 69 | parent = o.parent; | 
|---|
|  | 70 | rtnType.reset( maybeClone(o.rtnType.get()) ); | 
|---|
| [a2dbcff1] | 71 |  | 
|---|
| [4d59ff9] | 72 | return *this; | 
|---|
|  | 73 | } | 
|---|
| [a2dbcff1] | 74 | ProtoDump& operator= (ProtoDump&&) = delete; | 
|---|
| [3b3491b6] | 75 |  | 
|---|
|  | 76 | private: | 
|---|
|  | 77 | /// checks if this declaration is contained in the scope or one of its parents | 
|---|
|  | 78 | bool hasDecl( const std::string& s ) const { | 
|---|
|  | 79 | if ( decls.count( s ) ) return true; | 
|---|
|  | 80 | if ( parent ) return parent->hasDecl( s ); | 
|---|
|  | 81 | return false; | 
|---|
|  | 82 | } | 
|---|
|  | 83 |  | 
|---|
|  | 84 | /// adds a new declaration to this scope, providing it does not already exist | 
|---|
|  | 85 | void addDecl( const std::string& s ) { | 
|---|
|  | 86 | if ( ! hasDecl( s ) ) decls.insert( s ); | 
|---|
|  | 87 | } | 
|---|
|  | 88 |  | 
|---|
|  | 89 | /// adds a new expression to this scope | 
|---|
|  | 90 | void addExpr( const std::string& s ) { | 
|---|
| [b181639] | 91 | if ( ! s.empty() ) { exprs.emplace_back( s ); } | 
|---|
| [3b3491b6] | 92 | } | 
|---|
|  | 93 |  | 
|---|
|  | 94 | /// adds a new subscope to this scope, returning a reference | 
|---|
| [40cd873] | 95 | void addSub( PassVisitor<ProtoDump>&& sub ) { | 
|---|
|  | 96 | subs.emplace_back( std::move(sub.pass) ); | 
|---|
| [3b3491b6] | 97 | } | 
|---|
| [a2dbcff1] | 98 |  | 
|---|
| [3b3491b6] | 99 | /// Whether lists should be separated, terminated, or preceded by their separator | 
|---|
|  | 100 | enum septype { separated, terminated, preceded }; | 
|---|
|  | 101 |  | 
|---|
|  | 102 | /// builds space-separated list of types | 
|---|
|  | 103 | template<typename V> | 
|---|
| [a2dbcff1] | 104 | static void build( V& visitor, const std::list< Type* >& tys, std::stringstream& ss, | 
|---|
| [3b3491b6] | 105 | septype mode = separated ) { | 
|---|
|  | 106 | if ( tys.empty() ) return; | 
|---|
|  | 107 |  | 
|---|
|  | 108 | if ( mode == preceded ) { ss << ' '; } | 
|---|
|  | 109 |  | 
|---|
|  | 110 | auto it = tys.begin(); | 
|---|
|  | 111 | (*it)->accept( visitor ); | 
|---|
|  | 112 |  | 
|---|
|  | 113 | while ( ++it != tys.end() ) { | 
|---|
|  | 114 | ss << ' '; | 
|---|
|  | 115 | (*it)->accept( visitor ); | 
|---|
|  | 116 | } | 
|---|
|  | 117 |  | 
|---|
|  | 118 | if ( mode == terminated ) { ss << ' '; } | 
|---|
|  | 119 | } | 
|---|
|  | 120 |  | 
|---|
|  | 121 | /// builds list of types wrapped as tuple type | 
|---|
|  | 122 | template<typename V> | 
|---|
| [a2dbcff1] | 123 | static void buildAsTuple( V& visitor, const std::list< Type* >& tys, | 
|---|
| [3b3491b6] | 124 | std::stringstream& ss ) { | 
|---|
|  | 125 | switch ( tys.size() ) { | 
|---|
|  | 126 | case 0: ss << "#void"; break; | 
|---|
|  | 127 | case 1: tys.front()->accept( visitor ); break; | 
|---|
|  | 128 | default: | 
|---|
|  | 129 | ss << "#$" << tys.size() << '<'; | 
|---|
|  | 130 | build( visitor, tys, ss ); | 
|---|
|  | 131 | ss << '>'; | 
|---|
|  | 132 | break; | 
|---|
|  | 133 | } | 
|---|
|  | 134 | } | 
|---|
|  | 135 |  | 
|---|
|  | 136 | /// gets types from DWT list | 
|---|
|  | 137 | static std::list< Type* > from_decls( const std::list< DeclarationWithType* >& decls ) { | 
|---|
|  | 138 | std::list< Type* > tys; | 
|---|
|  | 139 | for ( auto decl : decls ) { tys.emplace_back( decl->get_type() ); } | 
|---|
|  | 140 | return tys; | 
|---|
|  | 141 | } | 
|---|
|  | 142 |  | 
|---|
|  | 143 | /// gets types from TypeExpr list | 
|---|
|  | 144 | static std::list< Type* > from_exprs( const std::list< Expression* >& exprs ) { | 
|---|
|  | 145 | std::list< Type* > tys; | 
|---|
|  | 146 | for ( auto expr : exprs ) { | 
|---|
|  | 147 | if ( TypeExpr* tyExpr = dynamic_cast<TypeExpr*>(expr) ) { | 
|---|
|  | 148 | tys.emplace_back( tyExpr->type ); | 
|---|
|  | 149 | } | 
|---|
|  | 150 | } | 
|---|
|  | 151 | return tys; | 
|---|
|  | 152 | } | 
|---|
|  | 153 |  | 
|---|
|  | 154 | /// builds prefixes for rp_name | 
|---|
|  | 155 | static std::string new_prefix( const std::string& old, const char* added ) { | 
|---|
|  | 156 | if ( old.empty() ) return std::string{"$"} + added; | 
|---|
|  | 157 | return old + added; | 
|---|
|  | 158 | } | 
|---|
|  | 159 |  | 
|---|
|  | 160 | /// shortens operator names | 
|---|
|  | 161 | static void op_name( const std::string& name, std::stringstream& ss ) { | 
|---|
|  | 162 | if ( name.compare( 0, 10, "_operator_" ) == 0 ) { | 
|---|
|  | 163 | ss << name.substr(10); | 
|---|
| [a2dbcff1] | 164 | } else if ( name.compare( "_constructor" ) == 0 | 
|---|
| [3b3491b6] | 165 | || name.compare( "_destructor" ) == 0 ) { | 
|---|
|  | 166 | ss << name.substr(1); | 
|---|
|  | 167 | } else if ( name.compare( 0, 11, "__operator_" ) == 0 ) { | 
|---|
|  | 168 | ss << name.substr(11); | 
|---|
|  | 169 | } else { | 
|---|
|  | 170 | ss << name; | 
|---|
|  | 171 | } | 
|---|
|  | 172 | } | 
|---|
|  | 173 |  | 
|---|
|  | 174 | /// replaces operators with resolv-proto names | 
|---|
| [a2dbcff1] | 175 | static void rp_name( const std::string& name, std::stringstream& ss, | 
|---|
| [3b3491b6] | 176 | std::string&& pre = "" ) { | 
|---|
|  | 177 | // safety check for anonymous names | 
|---|
|  | 178 | if ( name.empty() ) { | 
|---|
|  | 179 | ss << new_prefix(pre, "anon"); | 
|---|
|  | 180 | return; | 
|---|
|  | 181 | } | 
|---|
|  | 182 |  | 
|---|
|  | 183 | // replace operator names | 
|---|
| [60a8062] | 184 | const CodeGen::OperatorInfo * opInfo = CodeGen::operatorLookup( name ); | 
|---|
|  | 185 | if ( opInfo ) { | 
|---|
| [3b3491b6] | 186 | ss << new_prefix(pre, ""); | 
|---|
| [60a8062] | 187 | op_name( opInfo->outputName, ss ); | 
|---|
| [3b3491b6] | 188 | return; | 
|---|
| [a2dbcff1] | 189 | } | 
|---|
|  | 190 |  | 
|---|
| [3b3491b6] | 191 | // replace retval names | 
|---|
|  | 192 | if ( name.compare( 0, 8, "_retval_" ) == 0 ) { | 
|---|
|  | 193 | ss << new_prefix(pre, "rtn_"); | 
|---|
|  | 194 | op_name( name.substr(8), ss ); | 
|---|
|  | 195 | return; | 
|---|
|  | 196 | } | 
|---|
| [a2dbcff1] | 197 |  | 
|---|
| [4c42a5f] | 198 | // default to just name, with first character in lowercase | 
|---|
| [a2dbcff1] | 199 | ss << pre | 
|---|
| [4c42a5f] | 200 | << (char)std::tolower( static_cast<unsigned char>(name[0]) ) | 
|---|
|  | 201 | << (name.c_str() + 1); | 
|---|
| [3b3491b6] | 202 | } | 
|---|
|  | 203 |  | 
|---|
|  | 204 | /// ensures type inst names are uppercase | 
|---|
|  | 205 | static void ti_name( const std::string& name, std::stringstream& ss ) { | 
|---|
| [bb0f974] | 206 | // replace built-in wide character types with named types | 
|---|
|  | 207 | if ( name == "char16_t" || name == "char32_t" || name == "wchar_t" ) { | 
|---|
|  | 208 | ss << "#" << name; | 
|---|
|  | 209 | return; | 
|---|
|  | 210 | } | 
|---|
|  | 211 |  | 
|---|
|  | 212 | // strip leading underscore | 
|---|
| [4c42a5f] | 213 | unsigned i = 0; | 
|---|
|  | 214 | while ( i < name.size() && name[i] == '_' ) { ++i; } | 
|---|
|  | 215 | if ( i == name.size() ) { | 
|---|
|  | 216 | ss << "Anon"; | 
|---|
|  | 217 | return; | 
|---|
|  | 218 | } | 
|---|
| [bb0f974] | 219 |  | 
|---|
|  | 220 | std::string stripped = name.substr(i); | 
|---|
|  | 221 | // strip trailing "_generic_" from autogen names (avoids some user-generation issues) | 
|---|
|  | 222 | char generic[] = "_generic_"; size_t n_generic = sizeof(generic) - 1; | 
|---|
| [a2dbcff1] | 223 | if ( stripped.size() >= n_generic | 
|---|
| [bb0f974] | 224 | && stripped.substr( stripped.size() - n_generic ) == generic ) { | 
|---|
|  | 225 | stripped.resize( stripped.size() - n_generic ); | 
|---|
|  | 226 | } | 
|---|
|  | 227 |  | 
|---|
|  | 228 | // uppercase first character | 
|---|
|  | 229 | ss << (char)std::toupper( static_cast<unsigned char>(stripped[0]) ) | 
|---|
|  | 230 | << (stripped.c_str() + 1); | 
|---|
| [3b3491b6] | 231 | } | 
|---|
|  | 232 |  | 
|---|
|  | 233 | /// Visitor for printing types | 
|---|
|  | 234 | struct TypePrinter : public WithShortCircuiting, WithVisitorRef<TypePrinter>, WithGuards { | 
|---|
| [a4a000d] | 235 | std::stringstream& ss;                          ///< Output to print to | 
|---|
|  | 236 | const std::unordered_set<std::string>& closed;  ///< Closed type variables | 
|---|
|  | 237 | unsigned depth;                                 ///< Depth of nesting from root type | 
|---|
| [3b3491b6] | 238 |  | 
|---|
| [a2dbcff1] | 239 | TypePrinter( const std::unordered_set<std::string>& closed, std::stringstream& ss ) | 
|---|
| [a4a000d] | 240 | : ss(ss), closed(closed), depth(0) {} | 
|---|
| [3b3491b6] | 241 |  | 
|---|
|  | 242 | // basic type represented as integer type | 
|---|
|  | 243 | // TODO maybe hard-code conversion graph and make named type | 
|---|
|  | 244 | void previsit( BasicType* bt ) { ss << (int)bt->get_kind(); } | 
|---|
|  | 245 |  | 
|---|
| [4c42a5f] | 246 | // pointers (except function pointers) represented as generic type | 
|---|
|  | 247 | void previsit( PointerType* pt ) { | 
|---|
|  | 248 | if ( ! dynamic_cast<FunctionType*>(pt->base) ) { ss << "#$ptr<"; ++depth; } | 
|---|
|  | 249 | } | 
|---|
|  | 250 | void postvisit( PointerType* pt ) { | 
|---|
|  | 251 | if ( ! dynamic_cast<FunctionType*>(pt->base) ) { --depth; ss << '>'; } | 
|---|
|  | 252 | } | 
|---|
| [3b3491b6] | 253 |  | 
|---|
| [4c42a5f] | 254 | // arrays represented as generic pointers | 
|---|
| [b181639] | 255 | void previsit( ArrayType* at ) { | 
|---|
| [4c42a5f] | 256 | ss << "#$ptr<"; | 
|---|
| [b181639] | 257 | ++depth; | 
|---|
|  | 258 | at->base->accept( *visitor ); | 
|---|
|  | 259 | --depth; | 
|---|
|  | 260 | ss << '>'; | 
|---|
|  | 261 | visit_children = false; | 
|---|
|  | 262 | } | 
|---|
| [3b3491b6] | 263 |  | 
|---|
|  | 264 | // ignore top-level reference types, they're mostly transparent to resolution | 
|---|
|  | 265 | void previsit( ReferenceType* ) { | 
|---|
|  | 266 | if ( depth > 0 ) { ss << "#$ref<"; } | 
|---|
|  | 267 | ++depth; | 
|---|
|  | 268 | } | 
|---|
|  | 269 | void postvisit( ReferenceType* ) { | 
|---|
|  | 270 | --depth; | 
|---|
|  | 271 | if ( depth > 0 ) { ss << '>'; } | 
|---|
|  | 272 | } | 
|---|
|  | 273 |  | 
|---|
| [4c42a5f] | 274 | // print function types using prototype syntax | 
|---|
| [3b3491b6] | 275 | void previsit( FunctionType* ft ) { | 
|---|
| [4c42a5f] | 276 | ss << '['; | 
|---|
| [3b3491b6] | 277 | ++depth; | 
|---|
| [4c42a5f] | 278 | build( *visitor, from_decls( ft->returnVals ), ss, preceded ); | 
|---|
|  | 279 | ss << " : "; | 
|---|
|  | 280 | build( *visitor, from_decls( ft->parameters ), ss, terminated ); | 
|---|
| [3b3491b6] | 281 | --depth; | 
|---|
| [4c42a5f] | 282 | ss << ']'; | 
|---|
| [3b3491b6] | 283 | visit_children = false; | 
|---|
|  | 284 | } | 
|---|
|  | 285 |  | 
|---|
|  | 286 | private: | 
|---|
|  | 287 | // prints aggregate type name as NamedType with optional paramters | 
|---|
|  | 288 | void handleAggregate( ReferenceToType* at ) { | 
|---|
|  | 289 | ss << '#' << at->name; | 
|---|
|  | 290 | if ( ! at->parameters.empty() ) { | 
|---|
|  | 291 | ss << '<'; | 
|---|
|  | 292 | ++depth; | 
|---|
|  | 293 | build( *visitor, from_exprs( at->parameters ), ss ); | 
|---|
|  | 294 | --depth; | 
|---|
|  | 295 | ss << '>'; | 
|---|
|  | 296 | } | 
|---|
|  | 297 | visit_children = false; | 
|---|
|  | 298 | } | 
|---|
|  | 299 |  | 
|---|
|  | 300 | public: | 
|---|
|  | 301 | // handle aggregate types using NamedType | 
|---|
|  | 302 | void previsit( StructInstType* st ) { handleAggregate( st ); } | 
|---|
|  | 303 | void previsit( UnionInstType* ut ) { handleAggregate( ut ); } | 
|---|
|  | 304 |  | 
|---|
|  | 305 | // replace enums with int | 
|---|
| [fc134a48] | 306 | void previsit( EnumInstType* ) { | 
|---|
|  | 307 | // TODO: add the meaningful representation of typed int | 
|---|
|  | 308 | ss << (int)BasicType::SignedInt; | 
|---|
|  | 309 | } | 
|---|
| [3b3491b6] | 310 |  | 
|---|
|  | 311 | void previsit( TypeInstType* vt ) { | 
|---|
| [a4a000d] | 312 | // print closed variables as named types | 
|---|
|  | 313 | if ( closed.count( vt->name ) ) { ss << '#' << vt->name; } | 
|---|
|  | 314 | // otherwise make sure first letter is capitalized | 
|---|
|  | 315 | else { ti_name( vt->name, ss ); } | 
|---|
| [3b3491b6] | 316 | } | 
|---|
|  | 317 |  | 
|---|
|  | 318 | // flattens empty and singleton tuples | 
|---|
|  | 319 | void previsit( TupleType* tt ) { | 
|---|
|  | 320 | ++depth; | 
|---|
|  | 321 | buildAsTuple( *visitor, tt->types, ss ); | 
|---|
|  | 322 | --depth; | 
|---|
|  | 323 | visit_children = false; | 
|---|
|  | 324 | } | 
|---|
|  | 325 |  | 
|---|
| [04bdc26] | 326 | // TODO support variable args for functions | 
|---|
|  | 327 | void previsit( VarArgsType* ) { | 
|---|
|  | 328 | // only include varargs for top level (argument type) | 
|---|
|  | 329 | if ( depth == 0 ) { ss << "#$varargs"; } | 
|---|
|  | 330 | } | 
|---|
| [3b3491b6] | 331 |  | 
|---|
|  | 332 | // replace 0 and 1 with int | 
|---|
|  | 333 | // TODO support 0 and 1 with their proper type names and conversions | 
|---|
|  | 334 | void previsit( ZeroType* ) { ss << (int)BasicType::SignedInt; } | 
|---|
|  | 335 | void previsit( OneType* ) { ss << (int)BasicType::SignedInt; } | 
|---|
|  | 336 |  | 
|---|
|  | 337 | // only print void type if not at top level | 
|---|
|  | 338 | void previsit( VoidType* ) { | 
|---|
|  | 339 | if ( depth > 0 ) { ss << "#void"; } | 
|---|
|  | 340 | } | 
|---|
|  | 341 | }; | 
|---|
| [a2dbcff1] | 342 |  | 
|---|
| [3b3491b6] | 343 | /// builds description of function | 
|---|
|  | 344 | void build( const std::string& name, FunctionType* fnTy, std::stringstream& ss ) { | 
|---|
| [a4a000d] | 345 | PassVisitor<TypePrinter> printTy{ closed, ss }; | 
|---|
|  | 346 | // print return values | 
|---|
| [3b3491b6] | 347 | build( printTy, from_decls( fnTy->returnVals ), ss, terminated ); | 
|---|
| [a4a000d] | 348 | // print name | 
|---|
| [3b3491b6] | 349 | rp_name( name, ss ); | 
|---|
| [a4a000d] | 350 | // print parameters | 
|---|
| [3b3491b6] | 351 | build( printTy, from_decls( fnTy->parameters ), ss, preceded ); | 
|---|
| [a4a000d] | 352 | // print assertions | 
|---|
|  | 353 | for ( TypeDecl* tyvar : fnTy->forall ) { | 
|---|
|  | 354 | for ( DeclarationWithType* assn : tyvar->assertions ) { | 
|---|
| [a2dbcff1] | 355 | ss << " | "; | 
|---|
| [a4a000d] | 356 | build( assn->name, assn->get_type(), ss ); | 
|---|
|  | 357 | } | 
|---|
|  | 358 | } | 
|---|
| [3b3491b6] | 359 | } | 
|---|
|  | 360 |  | 
|---|
|  | 361 | /// builds description of a variable (falls back to function if function type) | 
|---|
|  | 362 | void build( const std::string& name, Type* ty, std::stringstream& ss ) { | 
|---|
|  | 363 | // ignore top-level references | 
|---|
|  | 364 | Type *norefs = ty->stripReferences(); | 
|---|
| [a2dbcff1] | 365 |  | 
|---|
| [3b3491b6] | 366 | // fall back to function declaration if function type | 
|---|
|  | 367 | if ( PointerType* pTy = dynamic_cast< PointerType* >(norefs) ) { | 
|---|
|  | 368 | if ( FunctionType* fnTy = dynamic_cast< FunctionType* >(pTy->base) ) { | 
|---|
|  | 369 | build( name, fnTy, ss ); | 
|---|
|  | 370 | return; | 
|---|
|  | 371 | } | 
|---|
|  | 372 | } else if ( FunctionType* fnTy = dynamic_cast< FunctionType* >(norefs) ) { | 
|---|
|  | 373 | build( name, fnTy, ss ); | 
|---|
|  | 374 | return; | 
|---|
|  | 375 | } | 
|---|
|  | 376 |  | 
|---|
| [4c42a5f] | 377 | // print variable declaration in prototype syntax | 
|---|
| [a4a000d] | 378 | PassVisitor<TypePrinter> printTy{ closed, ss }; | 
|---|
| [3b3491b6] | 379 | norefs->accept( printTy ); | 
|---|
| [4c42a5f] | 380 | ss << " &"; | 
|---|
| [3b3491b6] | 381 | rp_name( name, ss ); | 
|---|
|  | 382 | } | 
|---|
|  | 383 |  | 
|---|
|  | 384 | /// builds description of a field access | 
|---|
|  | 385 | void build( const std::string& name, AggregateDecl* agg, Type* ty, std::stringstream& ss ) { | 
|---|
|  | 386 | // ignore top-level references | 
|---|
|  | 387 | Type *norefs = ty->stripReferences(); | 
|---|
|  | 388 |  | 
|---|
|  | 389 | // print access as new field name | 
|---|
| [a4a000d] | 390 | PassVisitor<TypePrinter> printTy{ closed, ss }; | 
|---|
| [3b3491b6] | 391 | norefs->accept( printTy ); | 
|---|
|  | 392 | ss << ' '; | 
|---|
|  | 393 | rp_name( name, ss, "$field_" ); | 
|---|
|  | 394 | ss << " #" << agg->name; | 
|---|
|  | 395 | // handle type parameters | 
|---|
|  | 396 | if ( ! agg->parameters.empty() ) { | 
|---|
|  | 397 | ss << '<'; | 
|---|
|  | 398 | auto it = agg->parameters.begin(); | 
|---|
|  | 399 | while (true) { | 
|---|
|  | 400 | ti_name( (*it)->name, ss ); | 
|---|
|  | 401 | if ( ++it == agg->parameters.end() ) break; | 
|---|
|  | 402 | ss << ' '; | 
|---|
|  | 403 | } | 
|---|
|  | 404 | ss << '>'; | 
|---|
|  | 405 | } | 
|---|
|  | 406 | } | 
|---|
|  | 407 |  | 
|---|
|  | 408 | /// Visitor for printing expressions | 
|---|
|  | 409 | struct ExprPrinter : WithShortCircuiting, WithVisitorRef<ExprPrinter> { | 
|---|
|  | 410 | // TODO change interface to generate multiple expression candidates | 
|---|
| [a4a000d] | 411 | const std::unordered_set<std::string>& closed;  ///< set of closed type vars | 
|---|
|  | 412 | std::stringstream& ss;                          ///< Output to print to | 
|---|
| [3b3491b6] | 413 |  | 
|---|
| [a2dbcff1] | 414 | ExprPrinter( const std::unordered_set<std::string>& closed, std::stringstream& ss ) | 
|---|
| [a4a000d] | 415 | : closed(closed), ss(ss) {} | 
|---|
| [3b3491b6] | 416 |  | 
|---|
| [4c42a5f] | 417 | /// Names handled as name expressions | 
|---|
| [3b3491b6] | 418 | void previsit( NameExpr* expr ) { | 
|---|
| [4c42a5f] | 419 | ss << '&'; | 
|---|
| [3b3491b6] | 420 | rp_name( expr->name, ss ); | 
|---|
|  | 421 | } | 
|---|
|  | 422 |  | 
|---|
| [bb0f974] | 423 | /// Handle already-resolved variables as type constants | 
|---|
|  | 424 | void previsit( VariableExpr* expr ) { | 
|---|
|  | 425 | PassVisitor<TypePrinter> tyPrinter{ closed, ss }; | 
|---|
|  | 426 | expr->var->get_type()->accept( tyPrinter ); | 
|---|
|  | 427 | visit_children = false; | 
|---|
|  | 428 | } | 
|---|
|  | 429 |  | 
|---|
| [3b3491b6] | 430 | /// Calls handled as calls | 
|---|
|  | 431 | void previsit( UntypedExpr* expr ) { | 
|---|
|  | 432 | // TODO handle name extraction more generally | 
|---|
|  | 433 | NameExpr* name = dynamic_cast<NameExpr*>(expr->function); | 
|---|
|  | 434 |  | 
|---|
|  | 435 | // fall back on just resolving call to function name | 
|---|
|  | 436 | // TODO incorporate function type into resolv-proto | 
|---|
|  | 437 | if ( ! name ) { | 
|---|
|  | 438 | expr->function->accept( *visitor ); | 
|---|
|  | 439 | visit_children = false; | 
|---|
|  | 440 | return; | 
|---|
|  | 441 | } | 
|---|
|  | 442 |  | 
|---|
|  | 443 | rp_name( name->name, ss ); | 
|---|
|  | 444 | if ( expr->args.empty() ) { | 
|---|
|  | 445 | ss << "()"; | 
|---|
|  | 446 | } else { | 
|---|
|  | 447 | ss << "( "; | 
|---|
|  | 448 | auto it = expr->args.begin(); | 
|---|
|  | 449 | while (true) { | 
|---|
|  | 450 | (*it)->accept( *visitor ); | 
|---|
|  | 451 | if ( ++it == expr->args.end() ) break; | 
|---|
|  | 452 | ss << ' '; | 
|---|
|  | 453 | } | 
|---|
|  | 454 | ss << " )"; | 
|---|
|  | 455 | } | 
|---|
|  | 456 | visit_children = false; | 
|---|
|  | 457 | } | 
|---|
|  | 458 |  | 
|---|
| [bb0f974] | 459 | /// Already-resolved calls reduced to their type constant | 
|---|
|  | 460 | void previsit( ApplicationExpr* expr ) { | 
|---|
|  | 461 | PassVisitor<TypePrinter> tyPrinter{ closed, ss }; | 
|---|
|  | 462 | expr->result->accept( tyPrinter ); | 
|---|
| [4c42a5f] | 463 | visit_children = false; | 
|---|
|  | 464 | } | 
|---|
|  | 465 |  | 
|---|
| [3b3491b6] | 466 | /// Address-of handled as operator | 
|---|
|  | 467 | void previsit( AddressExpr* expr ) { | 
|---|
|  | 468 | ss << "$addr( "; | 
|---|
|  | 469 | expr->arg->accept( *visitor ); | 
|---|
|  | 470 | ss << " )"; | 
|---|
|  | 471 | visit_children = false; | 
|---|
|  | 472 | } | 
|---|
|  | 473 |  | 
|---|
| [b181639] | 474 | /// Casts replaced with result type | 
|---|
|  | 475 | /// TODO put cast target functions in, and add second expression for target | 
|---|
|  | 476 | void previsit( CastExpr* cast ) { | 
|---|
| [a4a000d] | 477 | PassVisitor<TypePrinter> tyPrinter{ closed, ss }; | 
|---|
| [b181639] | 478 | cast->result->accept( tyPrinter ); | 
|---|
|  | 479 | visit_children = false; | 
|---|
|  | 480 | } | 
|---|
| [a2dbcff1] | 481 |  | 
|---|
| [3b3491b6] | 482 | /// Member access handled as function from aggregate to member | 
|---|
|  | 483 | void previsit( UntypedMemberExpr* expr ) { | 
|---|
|  | 484 | // TODO handle name extraction more generally | 
|---|
|  | 485 | NameExpr* name = dynamic_cast<NameExpr*>(expr->member); | 
|---|
|  | 486 |  | 
|---|
|  | 487 | // fall back on just resolving call to member name | 
|---|
|  | 488 | // TODO incorporate function type into resolv-proto | 
|---|
|  | 489 | if ( ! name ) { | 
|---|
|  | 490 | expr->member->accept( *visitor ); | 
|---|
|  | 491 | visit_children = false; | 
|---|
|  | 492 | return; | 
|---|
|  | 493 | } | 
|---|
|  | 494 |  | 
|---|
|  | 495 | rp_name( name->name, ss, "$field_" ); | 
|---|
|  | 496 | ss << "( "; | 
|---|
|  | 497 | expr->aggregate->accept( *visitor ); | 
|---|
|  | 498 | ss << " )"; | 
|---|
|  | 499 | visit_children = false; | 
|---|
|  | 500 | } | 
|---|
|  | 501 |  | 
|---|
|  | 502 | /// Constant expression replaced by its type | 
|---|
|  | 503 | void previsit( ConstantExpr* expr ) { | 
|---|
| [a4a000d] | 504 | PassVisitor<TypePrinter> tyPrinter{ closed, ss }; | 
|---|
| [3b3491b6] | 505 | expr->constant.get_type()->accept( tyPrinter ); | 
|---|
|  | 506 | visit_children = false; | 
|---|
|  | 507 | } | 
|---|
|  | 508 |  | 
|---|
|  | 509 | /// sizeof( ... ), alignof( ... ), offsetof( ... ) replaced by unsigned long constant | 
|---|
|  | 510 | /// TODO extra expression to resolve argument | 
|---|
|  | 511 | void previsit( SizeofExpr* ) { | 
|---|
|  | 512 | ss << (int)BasicType::LongUnsignedInt; | 
|---|
|  | 513 | visit_children = false; | 
|---|
|  | 514 | } | 
|---|
|  | 515 | void previsit( AlignofExpr* ) { | 
|---|
|  | 516 | ss << (int)BasicType::LongUnsignedInt; | 
|---|
|  | 517 | visit_children = false; | 
|---|
|  | 518 | } | 
|---|
|  | 519 | void previsit( UntypedOffsetofExpr* ) { | 
|---|
|  | 520 | ss << (int)BasicType::LongUnsignedInt; | 
|---|
|  | 521 | visit_children = false; | 
|---|
|  | 522 | } | 
|---|
|  | 523 |  | 
|---|
|  | 524 | /// Logical expressions represented as operators | 
|---|
|  | 525 | void previsit( LogicalExpr* expr ) { | 
|---|
|  | 526 | ss << '$' << ( expr->get_isAnd() ? "and" : "or" ) << "( "; | 
|---|
|  | 527 | expr->arg1->accept( *visitor ); | 
|---|
|  | 528 | ss << ' '; | 
|---|
|  | 529 | expr->arg2->accept( *visitor ); | 
|---|
|  | 530 | ss << " )"; | 
|---|
|  | 531 | visit_children = false; | 
|---|
|  | 532 | } | 
|---|
|  | 533 |  | 
|---|
|  | 534 | /// Conditional expression represented as operator | 
|---|
|  | 535 | void previsit( ConditionalExpr* expr ) { | 
|---|
|  | 536 | ss << "$if( "; | 
|---|
|  | 537 | expr->arg1->accept( *visitor ); | 
|---|
|  | 538 | ss << ' '; | 
|---|
|  | 539 | expr->arg2->accept( *visitor ); | 
|---|
|  | 540 | ss << ' '; | 
|---|
|  | 541 | expr->arg3->accept( *visitor ); | 
|---|
|  | 542 | ss << " )"; | 
|---|
|  | 543 | visit_children = false; | 
|---|
|  | 544 | } | 
|---|
|  | 545 |  | 
|---|
|  | 546 | /// Comma expression represented as operator | 
|---|
|  | 547 | void previsit( CommaExpr* expr ) { | 
|---|
|  | 548 | ss << "$seq( "; | 
|---|
|  | 549 | expr->arg1->accept( *visitor ); | 
|---|
|  | 550 | ss << ' '; | 
|---|
|  | 551 | expr->arg2->accept( *visitor ); | 
|---|
|  | 552 | ss << " )"; | 
|---|
|  | 553 | visit_children = false; | 
|---|
|  | 554 | } | 
|---|
|  | 555 |  | 
|---|
|  | 556 | // TODO handle ignored ImplicitCopyCtorExpr and below | 
|---|
|  | 557 | }; | 
|---|
|  | 558 |  | 
|---|
| [4d59ff9] | 559 | void build( Initializer* init, std::stringstream& ss ) { | 
|---|
|  | 560 | if ( SingleInit* si = dynamic_cast<SingleInit*>(init) ) { | 
|---|
| [a4a000d] | 561 | PassVisitor<ExprPrinter> exprPrinter{ closed, ss }; | 
|---|
| [4d59ff9] | 562 | si->value->accept( exprPrinter ); | 
|---|
|  | 563 | ss << ' '; | 
|---|
|  | 564 | } else if ( ListInit* li = dynamic_cast<ListInit*>(init) ) { | 
|---|
|  | 565 | for ( Initializer* it : li->initializers ) { | 
|---|
|  | 566 | build( it, ss ); | 
|---|
|  | 567 | } | 
|---|
|  | 568 | } | 
|---|
|  | 569 | } | 
|---|
|  | 570 |  | 
|---|
|  | 571 | /// Adds an object initializer to the list of expressions | 
|---|
|  | 572 | void build( const std::string& name, Initializer* init, std::stringstream& ss ) { | 
|---|
| [bb0f974] | 573 | ss << "$constructor( &"; | 
|---|
| [4d59ff9] | 574 | rp_name( name, ss ); | 
|---|
| [bb0f974] | 575 | ss << ' '; | 
|---|
| [4d59ff9] | 576 | build( init, ss ); | 
|---|
|  | 577 | ss << ')'; | 
|---|
|  | 578 | } | 
|---|
|  | 579 |  | 
|---|
|  | 580 | /// Adds a return expression to the list of expressions | 
|---|
|  | 581 | void build( Type* rtnType, Expression* expr, std::stringstream& ss ) { | 
|---|
|  | 582 | ss << "$constructor( "; | 
|---|
| [a4a000d] | 583 | PassVisitor<TypePrinter> tyPrinter{ closed, ss }; | 
|---|
| [4d59ff9] | 584 | rtnType->accept( tyPrinter ); | 
|---|
|  | 585 | ss << ' '; | 
|---|
| [a4a000d] | 586 | PassVisitor<ExprPrinter> exprPrinter{ closed, ss }; | 
|---|
| [4d59ff9] | 587 | expr->accept( exprPrinter ); | 
|---|
|  | 588 | ss << " )"; | 
|---|
|  | 589 | } | 
|---|
|  | 590 |  | 
|---|
| [3b3491b6] | 591 | /// Adds all named declarations in a list to the local scope | 
|---|
|  | 592 | void addAll( const std::list<DeclarationWithType*>& decls ) { | 
|---|
|  | 593 | for ( auto decl : decls ) { | 
|---|
|  | 594 | // skip anonymous decls | 
|---|
|  | 595 | if ( decl->name.empty() ) continue; | 
|---|
|  | 596 |  | 
|---|
|  | 597 | // handle objects | 
|---|
|  | 598 | if ( ObjectDecl* obj = dynamic_cast< ObjectDecl* >( decl ) ) { | 
|---|
|  | 599 | previsit( obj ); | 
|---|
|  | 600 | } | 
|---|
|  | 601 | } | 
|---|
|  | 602 | } | 
|---|
|  | 603 |  | 
|---|
|  | 604 | /// encode field access as function | 
|---|
|  | 605 | void addAggregateFields( AggregateDecl* agg ) { | 
|---|
|  | 606 | // make field names functions | 
|---|
|  | 607 | for ( Declaration* member : agg->members ) { | 
|---|
|  | 608 | if ( ObjectDecl* obj = dynamic_cast< ObjectDecl* >(member) ) { | 
|---|
|  | 609 | std::stringstream ss; | 
|---|
|  | 610 | build( obj->name, agg, obj->type, ss ); | 
|---|
|  | 611 | addDecl( ss.str() ); | 
|---|
|  | 612 | } | 
|---|
|  | 613 | } | 
|---|
|  | 614 |  | 
|---|
|  | 615 | visit_children = false; | 
|---|
|  | 616 | } | 
|---|
|  | 617 |  | 
|---|
|  | 618 | public: | 
|---|
|  | 619 | void previsit( ObjectDecl *obj ) { | 
|---|
|  | 620 | // add variable as declaration | 
|---|
|  | 621 | std::stringstream ss; | 
|---|
|  | 622 | build( obj->name, obj->type, ss ); | 
|---|
|  | 623 | addDecl( ss.str() ); | 
|---|
| [4d59ff9] | 624 |  | 
|---|
|  | 625 | // add initializer as expression if applicable | 
|---|
|  | 626 | if ( obj->init ) { | 
|---|
|  | 627 | std::stringstream ss; | 
|---|
|  | 628 | build( obj->name, obj->init, ss ); | 
|---|
|  | 629 | addExpr( ss.str() ); | 
|---|
|  | 630 | } | 
|---|
| [3b3491b6] | 631 | } | 
|---|
|  | 632 |  | 
|---|
|  | 633 | void previsit( FunctionDecl *decl ) { | 
|---|
| [4c42a5f] | 634 | // skip decls with ftype parameters | 
|---|
|  | 635 | for ( TypeDecl* tyvar : decl->type->forall ) { | 
|---|
|  | 636 | if ( tyvar->get_kind() == TypeDecl::Ftype ) { | 
|---|
|  | 637 | visit_children = false; | 
|---|
|  | 638 | return; | 
|---|
|  | 639 | } | 
|---|
|  | 640 | } | 
|---|
|  | 641 |  | 
|---|
| [3b3491b6] | 642 | // add function as declaration | 
|---|
|  | 643 | std::stringstream ss; | 
|---|
|  | 644 | build( decl->name, decl->type, ss ); | 
|---|
|  | 645 | addDecl( ss.str() ); | 
|---|
|  | 646 |  | 
|---|
|  | 647 | // add body if available | 
|---|
|  | 648 | if ( decl->statements ) { | 
|---|
| [4d59ff9] | 649 | std::list<Type*> rtns = from_decls( decl->type->returnVals ); | 
|---|
|  | 650 | Type* rtn = nullptr; | 
|---|
|  | 651 | if ( rtns.size() == 1 ) { | 
|---|
|  | 652 | if ( ! dynamic_cast<VoidType*>(rtns.front()) ) rtn = rtns.front()->clone(); | 
|---|
|  | 653 | } else if ( rtns.size() > 1 ) { | 
|---|
|  | 654 | rtn = new TupleType{ Type::Qualifiers{}, rtns }; | 
|---|
|  | 655 | } | 
|---|
|  | 656 | PassVisitor<ProtoDump> body{ this, rtn }; | 
|---|
| [a4a000d] | 657 |  | 
|---|
|  | 658 | for ( TypeDecl* tyvar : decl->type->forall ) { | 
|---|
|  | 659 | // add set of "closed" types to body so that it can print them as NamedType | 
|---|
|  | 660 | body.pass.closed.insert( tyvar->name ); | 
|---|
|  | 661 |  | 
|---|
|  | 662 | // add assertions to local scope as declarations as well | 
|---|
|  | 663 | for ( DeclarationWithType* assn : tyvar->assertions ) { | 
|---|
|  | 664 | assn->accept( body ); | 
|---|
|  | 665 | } | 
|---|
|  | 666 | } | 
|---|
| [a2dbcff1] | 667 |  | 
|---|
| [3b3491b6] | 668 | // add named parameters and returns to local scope | 
|---|
|  | 669 | body.pass.addAll( decl->type->returnVals ); | 
|---|
|  | 670 | body.pass.addAll( decl->type->parameters ); | 
|---|
|  | 671 |  | 
|---|
|  | 672 | // add contents of function to new scope | 
|---|
|  | 673 | decl->statements->accept( body ); | 
|---|
| [40cd873] | 674 |  | 
|---|
|  | 675 | // store sub-scope | 
|---|
|  | 676 | addSub( std::move(body) ); | 
|---|
| [3b3491b6] | 677 | } | 
|---|
|  | 678 |  | 
|---|
|  | 679 | visit_children = false; | 
|---|
|  | 680 | } | 
|---|
|  | 681 |  | 
|---|
|  | 682 | void previsit( StructDecl* sd ) { addAggregateFields(sd); } | 
|---|
|  | 683 | void previsit( UnionDecl* ud ) { addAggregateFields(ud); } | 
|---|
| [a2dbcff1] | 684 |  | 
|---|
| [3b3491b6] | 685 | void previsit( EnumDecl* ed ) { | 
|---|
| [a2dbcff1] | 686 | std::unique_ptr<Type> eType = | 
|---|
| [3b3491b6] | 687 | std::make_unique<BasicType>( Type::Qualifiers{}, BasicType::SignedInt ); | 
|---|
| [a2dbcff1] | 688 |  | 
|---|
| [3b3491b6] | 689 | // add field names directly to enclosing scope | 
|---|
|  | 690 | for ( Declaration* member : ed->members ) { | 
|---|
|  | 691 | if ( ObjectDecl* obj = dynamic_cast< ObjectDecl* >(member) ) { | 
|---|
|  | 692 | previsit(obj); | 
|---|
|  | 693 | } | 
|---|
|  | 694 | } | 
|---|
|  | 695 |  | 
|---|
|  | 696 | visit_children = false; | 
|---|
|  | 697 | } | 
|---|
|  | 698 |  | 
|---|
| [4d59ff9] | 699 | void previsit( ReturnStmt* stmt ) { | 
|---|
|  | 700 | // do nothing for void-returning functions or statements returning nothing | 
|---|
|  | 701 | if ( ! rtnType || ! stmt->expr ) return; | 
|---|
|  | 702 |  | 
|---|
|  | 703 | // otherwise construct the return type from the expression | 
|---|
|  | 704 | std::stringstream ss; | 
|---|
|  | 705 | build( rtnType.get(), stmt->expr, ss ); | 
|---|
|  | 706 | addExpr( ss.str() ); | 
|---|
|  | 707 | visit_children = false; | 
|---|
|  | 708 | } | 
|---|
|  | 709 |  | 
|---|
| [bb0f974] | 710 | void previsit( AsmStmt* ) { | 
|---|
|  | 711 | // skip asm statements | 
|---|
|  | 712 | visit_children = false; | 
|---|
|  | 713 | } | 
|---|
|  | 714 |  | 
|---|
| [3b3491b6] | 715 | void previsit( Expression* expr ) { | 
|---|
|  | 716 | std::stringstream ss; | 
|---|
| [a4a000d] | 717 | PassVisitor<ExprPrinter> exPrinter{ closed, ss }; | 
|---|
| [3b3491b6] | 718 | expr->accept( exPrinter ); | 
|---|
|  | 719 | addExpr( ss.str() ); | 
|---|
|  | 720 | visit_children = false; | 
|---|
|  | 721 | } | 
|---|
|  | 722 |  | 
|---|
| [a4a000d] | 723 | /// Print non-prelude global declarations for resolv proto | 
|---|
|  | 724 | void printGlobals() const { | 
|---|
| [bb0f974] | 725 | std::cout << "#$ptr<T> $addr T" << std::endl;  // &? | 
|---|
| [a4a000d] | 726 | int i = (int)BasicType::SignedInt; | 
|---|
|  | 727 | std::cout << i << " $and " << i << ' ' << i << std::endl;  // ?&&? | 
|---|
|  | 728 | std::cout << i << " $or " << i << ' ' << i << std::endl;  // ?||? | 
|---|
|  | 729 | std::cout << "T $if " << i << " T T" << std::endl; // ternary operator | 
|---|
|  | 730 | std::cout << "T $seq X T" << std::endl;  // ?,? | 
|---|
|  | 731 | } | 
|---|
|  | 732 |  | 
|---|
| [3b3491b6] | 733 | public: | 
|---|
|  | 734 | /// Prints this ProtoDump instance | 
|---|
|  | 735 | void print(unsigned indent = 0) const { | 
|---|
| [a4a000d] | 736 | // print globals at root level | 
|---|
|  | 737 | if ( ! parent ) printGlobals(); | 
|---|
| [3b3491b6] | 738 | // print decls | 
|---|
| [a4a000d] | 739 | std::string tab( indent, '\t' ); | 
|---|
| [3b3491b6] | 740 | for ( const std::string& d : decls ) { | 
|---|
|  | 741 | std::cout << tab << d << std::endl; | 
|---|
|  | 742 | } | 
|---|
|  | 743 | // print divider | 
|---|
| [4d59ff9] | 744 | std::cout << '\n' << tab << "%%\n" << std::endl; | 
|---|
| [3b3491b6] | 745 | // print top-level expressions | 
|---|
|  | 746 | for ( const std::string& e : exprs ) { | 
|---|
|  | 747 | std::cout << tab << e << std::endl; | 
|---|
|  | 748 | } | 
|---|
|  | 749 | // print child scopes | 
|---|
|  | 750 | ++indent; | 
|---|
| [bc179fd3] | 751 | for ( const ProtoDump & s : subs ) { | 
|---|
| [3b3491b6] | 752 | std::cout << tab << '{' << std::endl; | 
|---|
| [bc179fd3] | 753 | s.print( indent ); | 
|---|
| [3b3491b6] | 754 | std::cout << tab << '}' << std::endl; | 
|---|
|  | 755 | } | 
|---|
|  | 756 | } | 
|---|
|  | 757 | }; | 
|---|
|  | 758 |  | 
|---|
|  | 759 | void dumpAsResolvProto( std::list< Declaration * > &translationUnit ) { | 
|---|
|  | 760 | PassVisitor<ProtoDump> dump; | 
|---|
|  | 761 | acceptAll( translationUnit, dump ); | 
|---|
|  | 762 | dump.pass.print(); | 
|---|
|  | 763 | } | 
|---|
|  | 764 |  | 
|---|
|  | 765 | }  // namespace CodeTools | 
|---|
|  | 766 |  | 
|---|
|  | 767 | // Local Variables: // | 
|---|
|  | 768 | // tab-width: 4 // | 
|---|
|  | 769 | // mode: c++ // | 
|---|
|  | 770 | // compile-command: "make install" // | 
|---|
|  | 771 | // End: // | 
|---|