source: src/SynTree/Expression.h@ ae42f2a

ADT aaron-thesis arm-eh ast-experimental cleanup-dtors ctor deferred_resn demangler enum forall-pointer-decay gc_noraii jacob/cs343-translation jenkins-sandbox memory new-ast new-ast-unique-expr new-env no_list persistent-indexer pthread-emulation qualifiedEnum resolv-new with_gc
Last change on this file since ae42f2a was 2a4b088, checked in by Aaron Moss <a3moss@…>, 10 years ago

Make offsetof expressions work

  • Property mode set to 100644
File size: 21.1 KB
Line 
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// Expression.h --
8//
9// Author : Richard C. Bilson
10// Created On : Mon May 18 07:44:20 2015
11// Last Modified By : Rob Schluntz
12// Last Modified On : Wed Dec 09 14:10:21 2015
13// Update Count : 19
14//
15
16#ifndef EXPRESSION_H
17#define EXPRESSION_H
18
19#include <map>
20#include "SynTree.h"
21#include "Visitor.h"
22#include "Mutator.h"
23#include "Constant.h"
24
25/// Expression is the root type for all expressions
26class Expression {
27 public:
28 Expression(Expression *_aname = 0 );
29 Expression( const Expression &other );
30 virtual ~Expression();
31
32 std::list<Type *>& get_results() { return results; }
33 void add_result( Type *t );
34
35 TypeSubstitution *get_env() const { return env; }
36 void set_env( TypeSubstitution *newValue ) { env = newValue; }
37 Expression *get_argName() const { return argName; }
38 void set_argName( Expression *name ) { argName = name; }
39
40 virtual Expression *clone() const = 0;
41 virtual void accept( Visitor &v ) = 0;
42 virtual Expression *acceptMutator( Mutator &m ) = 0;
43 virtual void print( std::ostream &os, int indent = 0 ) const;
44 protected:
45 std::list<Type *> results;
46 TypeSubstitution *env;
47 Expression* argName; // if expression is used as an argument, it can be "designated" by this name
48};
49
50/// ParamEntry contains the i.d. of a declaration and a type that is derived from that declaration,
51/// but subject to decay-to-pointer and type parameter renaming
52struct ParamEntry {
53 ParamEntry(): decl( 0 ), actualType( 0 ), formalType( 0 ), expr( 0 ) {}
54 ParamEntry( UniqueId decl, Type *actualType, Type *formalType, Expression* expr ): decl( decl ), actualType( actualType ), formalType( formalType ), expr( expr ) {}
55 ParamEntry( const ParamEntry &other );
56 ~ParamEntry();
57 ParamEntry &operator=( const ParamEntry &other );
58
59 UniqueId decl;
60 Type *actualType;
61 Type *formalType;
62 Expression* expr;
63};
64
65typedef std::map< UniqueId, ParamEntry > InferredParams;
66
67/// ApplicationExpr represents the application of a function to a set of parameters. This is the
68/// result of running an UntypedExpr through the expression analyzer.
69class ApplicationExpr : public Expression {
70 public:
71 ApplicationExpr( Expression *function );
72 ApplicationExpr( const ApplicationExpr &other );
73 virtual ~ApplicationExpr();
74
75 Expression *get_function() const { return function; }
76 void set_function( Expression *newValue ) { function = newValue; }
77 std::list<Expression *>& get_args() { return args; }
78 InferredParams &get_inferParams() { return inferParams; }
79
80 virtual ApplicationExpr *clone() const { return new ApplicationExpr( *this ); }
81 virtual void accept( Visitor &v ) { v.visit( this ); }
82 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
83 virtual void print( std::ostream &os, int indent = 0 ) const;
84 private:
85 Expression *function;
86 std::list<Expression *> args;
87 InferredParams inferParams;
88};
89
90/// UntypedExpr represents the application of a function to a set of parameters, but where the
91/// particular overload for the function name has not yet been determined. Most operators are
92/// converted into functional form automatically, to permit operator overloading.
93class UntypedExpr : public Expression {
94 public:
95 UntypedExpr( Expression *function, Expression *_aname = 0 );
96 UntypedExpr( const UntypedExpr &other );
97 UntypedExpr( Expression *function, std::list<Expression *> &args, Expression *_aname = 0 );
98 virtual ~UntypedExpr();
99
100 Expression *get_function() const { return function; }
101 void set_function( Expression *newValue ) { function = newValue; }
102
103 void set_args( std::list<Expression *> &listArgs ) { args = listArgs; }
104 std::list<Expression*>::iterator begin_args() { return args.begin(); }
105 std::list<Expression*>::iterator end_args() { return args.end(); }
106 std::list<Expression*>& get_args() { return args; }
107
108 virtual UntypedExpr *clone() const { return new UntypedExpr( *this ); }
109 virtual void accept( Visitor &v ) { v.visit( this ); }
110 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
111 virtual void print( std::ostream &os, int indent = 0 ) const;
112 virtual void printArgs(std::ostream &os, int indent = 0) const;
113 private:
114 Expression *function;
115 std::list<Expression*> args;
116};
117
118/// NameExpr contains a name whose meaning is still not determined
119class NameExpr : public Expression {
120 public:
121 NameExpr( std::string name, Expression *_aname = 0 );
122 NameExpr( const NameExpr &other );
123 virtual ~NameExpr();
124
125 const std::string &get_name() const { return name; }
126 void set_name( std::string newValue ) { name = newValue; }
127
128 virtual NameExpr *clone() const { return new NameExpr( *this ); }
129 virtual void accept( Visitor &v ) { v.visit( this ); }
130 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
131 virtual void print( std::ostream &os, int indent = 0 ) const;
132 private:
133 std::string name;
134};
135
136// The following classes are used to represent expression types that cannot be converted into
137// function-call format.
138
139/// AddressExpr represents a address-of expression, e.g. &e
140class AddressExpr : public Expression {
141 public:
142 AddressExpr( Expression *arg, Expression *_aname = 0 );
143 AddressExpr( const AddressExpr &other );
144 virtual ~AddressExpr();
145
146 Expression *get_arg() const { return arg; }
147 void set_arg(Expression *newValue ) { arg = newValue; }
148
149 virtual AddressExpr *clone() const { return new AddressExpr( *this ); }
150 virtual void accept( Visitor &v ) { v.visit( this ); }
151 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
152 virtual void print( std::ostream &os, int indent = 0 ) const;
153 private:
154 Expression *arg;
155};
156
157class LabelAddressExpr : public Expression {
158 public:
159 LabelAddressExpr( Expression *arg );
160 LabelAddressExpr( const AddressExpr &other );
161 virtual ~LabelAddressExpr();
162
163 Expression *get_arg() const { return arg; }
164 void set_arg(Expression *newValue ) { arg = newValue; }
165
166 virtual LabelAddressExpr *clone() const { return new LabelAddressExpr( *this ); }
167 virtual void accept( Visitor &v ) { v.visit( this ); }
168 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
169 virtual void print( std::ostream &os, int indent = 0 ) const;
170 private:
171 Expression *arg;
172};
173
174/// CastExpr represents a type cast expression, e.g. (int)e
175class CastExpr : public Expression {
176 public:
177 CastExpr( Expression *arg, Expression *_aname = 0 );
178 CastExpr( Expression *arg, Type *toType, Expression *_aname = 0 );
179 CastExpr( const CastExpr &other );
180 virtual ~CastExpr();
181
182 Expression *get_arg() const { return arg; }
183 void set_arg(Expression *newValue ) { arg = newValue; }
184
185 virtual CastExpr *clone() const { return new CastExpr( *this ); }
186 virtual void accept( Visitor &v ) { v.visit( this ); }
187 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
188 virtual void print( std::ostream &os, int indent = 0 ) const;
189 private:
190 Expression *arg;
191};
192
193/// UntypedMemberExpr represents a member selection operation, e.g. q.p before processing by the expression analyzer
194class UntypedMemberExpr : public Expression {
195 public:
196 UntypedMemberExpr( std::string member, Expression *aggregate, Expression *_aname = 0 );
197 UntypedMemberExpr( const UntypedMemberExpr &other );
198 virtual ~UntypedMemberExpr();
199
200 std::string get_member() const { return member; }
201 void set_member( const std::string &newValue ) { member = newValue; }
202 Expression *get_aggregate() const { return aggregate; }
203 void set_aggregate( Expression *newValue ) { aggregate = newValue; }
204
205 virtual UntypedMemberExpr *clone() const { return new UntypedMemberExpr( *this ); }
206 virtual void accept( Visitor &v ) { v.visit( this ); }
207 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
208 virtual void print( std::ostream &os, int indent = 0 ) const;
209 private:
210 std::string member;
211 Expression *aggregate;
212};
213
214/// MemberExpr represents a member selection operation, e.g. q.p after processing by the expression analyzer
215class MemberExpr : public Expression {
216 public:
217 MemberExpr( DeclarationWithType *member, Expression *aggregate, Expression *_aname = 0 );
218 MemberExpr( const MemberExpr &other );
219 virtual ~MemberExpr();
220
221 DeclarationWithType *get_member() const { return member; }
222 void set_member( DeclarationWithType *newValue ) { member = newValue; }
223 Expression *get_aggregate() const { return aggregate; }
224 void set_aggregate( Expression *newValue ) { aggregate = newValue; }
225
226 virtual MemberExpr *clone() const { return new MemberExpr( *this ); }
227 virtual void accept( Visitor &v ) { v.visit( this ); }
228 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
229 virtual void print( std::ostream &os, int indent = 0 ) const;
230 private:
231 DeclarationWithType *member;
232 Expression *aggregate;
233};
234
235/// VariableExpr represents an expression that simply refers to the value of a named variable
236class VariableExpr : public Expression {
237 public:
238 VariableExpr( DeclarationWithType *var, Expression *_aname = 0 );
239 VariableExpr( const VariableExpr &other );
240 virtual ~VariableExpr();
241
242 DeclarationWithType *get_var() const { return var; }
243 void set_var( DeclarationWithType *newValue ) { var = newValue; }
244
245 virtual VariableExpr *clone() const { return new VariableExpr( *this ); }
246 virtual void accept( Visitor &v ) { v.visit( this ); }
247 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
248 virtual void print( std::ostream &os, int indent = 0 ) const;
249 private:
250 DeclarationWithType *var;
251};
252
253/// ConstantExpr represents an expression that simply refers to the value of a constant
254class ConstantExpr : public Expression {
255 public:
256 ConstantExpr( Constant constant, Expression *_aname = 0 );
257 ConstantExpr( const ConstantExpr &other );
258 virtual ~ConstantExpr();
259
260 Constant *get_constant() { return &constant; }
261 void set_constant( const Constant &newValue ) { constant = newValue; }
262
263 virtual ConstantExpr *clone() const { return new ConstantExpr( *this ); }
264 virtual void accept( Visitor &v ) { v.visit( this ); }
265 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
266 virtual void print( std::ostream &os, int indent = 0 ) const;
267 private:
268 Constant constant;
269};
270
271/// SizeofExpr represents a sizeof expression (could be sizeof(int) or sizeof 3+4)
272class SizeofExpr : public Expression {
273 public:
274 SizeofExpr( Expression *expr, Expression *_aname = 0 );
275 SizeofExpr( const SizeofExpr &other );
276 SizeofExpr( Type *type, Expression *_aname = 0 );
277 virtual ~SizeofExpr();
278
279 Expression *get_expr() const { return expr; }
280 void set_expr( Expression *newValue ) { expr = newValue; }
281 Type *get_type() const { return type; }
282 void set_type( Type *newValue ) { type = newValue; }
283 bool get_isType() const { return isType; }
284 void set_isType( bool newValue ) { isType = newValue; }
285
286 virtual SizeofExpr *clone() const { return new SizeofExpr( *this ); }
287 virtual void accept( Visitor &v ) { v.visit( this ); }
288 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
289 virtual void print( std::ostream &os, int indent = 0 ) const;
290 private:
291 Expression *expr;
292 Type *type;
293 bool isType;
294};
295
296/// AlignofExpr represents an alignof expression
297class AlignofExpr : public Expression {
298 public:
299 AlignofExpr( Expression *expr, Expression *_aname = 0 );
300 AlignofExpr( const AlignofExpr &other );
301 AlignofExpr( Type *type, Expression *_aname = 0 );
302 virtual ~AlignofExpr();
303
304 Expression *get_expr() const { return expr; }
305 void set_expr( Expression *newValue ) { expr = newValue; }
306 Type *get_type() const { return type; }
307 void set_type( Type *newValue ) { type = newValue; }
308 bool get_isType() const { return isType; }
309 void set_isType( bool newValue ) { isType = newValue; }
310
311 virtual AlignofExpr *clone() const { return new AlignofExpr( *this ); }
312 virtual void accept( Visitor &v ) { v.visit( this ); }
313 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
314 virtual void print( std::ostream &os, int indent = 0 ) const;
315 private:
316 Expression *expr;
317 Type *type;
318 bool isType;
319};
320
321/// UntypedOffsetofExpr represents an offsetof expression before resolution
322class UntypedOffsetofExpr : public Expression {
323 public:
324 UntypedOffsetofExpr( Type *type, const std::string &member, Expression *_aname = 0 );
325 UntypedOffsetofExpr( const UntypedOffsetofExpr &other );
326 virtual ~UntypedOffsetofExpr();
327
328 std::string get_member() const { return member; }
329 void set_member( const std::string &newValue ) { member = newValue; }
330 Type *get_type() const { return type; }
331 void set_type( Type *newValue ) { type = newValue; }
332
333 virtual UntypedOffsetofExpr *clone() const { return new UntypedOffsetofExpr( *this ); }
334 virtual void accept( Visitor &v ) { v.visit( this ); }
335 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
336 virtual void print( std::ostream &os, int indent = 0 ) const;
337 private:
338 Type *type;
339 std::string member;
340};
341
342/// OffsetofExpr represents an offsetof expression
343class OffsetofExpr : public Expression {
344 public:
345 OffsetofExpr( Type *type, DeclarationWithType *member, Expression *_aname = 0 );
346 OffsetofExpr( const OffsetofExpr &other );
347 virtual ~OffsetofExpr();
348
349 Type *get_type() const { return type; }
350 void set_type( Type *newValue ) { type = newValue; }
351 DeclarationWithType *get_member() const { return member; }
352 void set_member( DeclarationWithType *newValue ) { member = newValue; }
353
354 virtual OffsetofExpr *clone() const { return new OffsetofExpr( *this ); }
355 virtual void accept( Visitor &v ) { v.visit( this ); }
356 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
357 virtual void print( std::ostream &os, int indent = 0 ) const;
358 private:
359 Type *type;
360 DeclarationWithType *member;
361};
362
363/// AttrExpr represents an @attribute expression (like sizeof, but user-defined)
364class AttrExpr : public Expression {
365 public:
366 AttrExpr(Expression *attr, Expression *expr, Expression *_aname = 0 );
367 AttrExpr( const AttrExpr &other );
368 AttrExpr( Expression *attr, Type *type, Expression *_aname = 0 );
369 virtual ~AttrExpr();
370
371 Expression *get_attr() const { return attr; }
372 void set_attr( Expression *newValue ) { attr = newValue; }
373 Expression *get_expr() const { return expr; }
374 void set_expr( Expression *newValue ) { expr = newValue; }
375 Type *get_type() const { return type; }
376 void set_type( Type *newValue ) { type = newValue; }
377 bool get_isType() const { return isType; }
378 void set_isType( bool newValue ) { isType = newValue; }
379
380 virtual AttrExpr *clone() const { return new AttrExpr( *this ); }
381 virtual void accept( Visitor &v ) { v.visit( this ); }
382 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
383 virtual void print( std::ostream &os, int indent = 0 ) const;
384 private:
385 Expression *attr;
386 Expression *expr;
387 Type *type;
388 bool isType;
389};
390
391/// LogicalExpr represents a short-circuit boolean expression (&& or ||)
392class LogicalExpr : public Expression {
393 public:
394 LogicalExpr( Expression *arg1, Expression *arg2, bool andp = true, Expression *_aname = 0 );
395 LogicalExpr( const LogicalExpr &other );
396 virtual ~LogicalExpr();
397
398 bool get_isAnd() const { return isAnd; }
399 Expression *get_arg1() { return arg1; }
400 void set_arg1( Expression *newValue ) { arg1 = newValue; }
401 Expression *get_arg2() const { return arg2; }
402 void set_arg2( Expression *newValue ) { arg2 = newValue; }
403
404 virtual LogicalExpr *clone() const { return new LogicalExpr( *this ); }
405 virtual void accept( Visitor &v ) { v.visit( this ); }
406 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
407 virtual void print( std::ostream &os, int indent = 0 ) const;
408 private:
409 Expression *arg1;
410 Expression *arg2;
411 bool isAnd;
412};
413
414/// ConditionalExpr represents the three-argument conditional ( p ? a : b )
415class ConditionalExpr : public Expression {
416 public:
417 ConditionalExpr( Expression *arg1, Expression *arg2, Expression *arg3, Expression *_aname = 0 );
418 ConditionalExpr( const ConditionalExpr &other );
419 virtual ~ConditionalExpr();
420
421 Expression *get_arg1() const { return arg1; }
422 void set_arg1( Expression *newValue ) { arg1 = newValue; }
423 Expression *get_arg2() const { return arg2; }
424 void set_arg2( Expression *newValue ) { arg2 = newValue; }
425 Expression *get_arg3() const { return arg3; }
426 void set_arg3( Expression *newValue ) { arg3 = newValue; }
427
428 virtual ConditionalExpr *clone() const { return new ConditionalExpr( *this ); }
429 virtual void accept( Visitor &v ) { v.visit( this ); }
430 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
431 virtual void print( std::ostream &os, int indent = 0 ) const;
432 private:
433 Expression *arg1;
434 Expression *arg2;
435 Expression *arg3;
436};
437
438/// CommaExpr represents the sequence operator ( a, b )
439class CommaExpr : public Expression {
440 public:
441 CommaExpr( Expression *arg1, Expression *arg2, Expression *_aname = 0 );
442 CommaExpr( const CommaExpr &other );
443 virtual ~CommaExpr();
444
445 Expression *get_arg1() const { return arg1; }
446 void set_arg1( Expression *newValue ) { arg1 = newValue; }
447 Expression *get_arg2() const { return arg2; }
448 void set_arg2( Expression *newValue ) { arg2 = newValue; }
449
450 virtual CommaExpr *clone() const { return new CommaExpr( *this ); }
451 virtual void accept( Visitor &v ) { v.visit( this ); }
452 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
453 virtual void print( std::ostream &os, int indent = 0 ) const;
454 private:
455 Expression *arg1;
456 Expression *arg2;
457};
458
459/// TupleExpr represents a tuple expression ( [a, b, c] )
460class TupleExpr : public Expression {
461 public:
462 TupleExpr( Expression *_aname = 0 );
463 TupleExpr( const TupleExpr &other );
464 virtual ~TupleExpr();
465
466 void set_exprs( std::list<Expression*> newValue ) { exprs = newValue; }
467 std::list<Expression*>& get_exprs() { return exprs; }
468
469 virtual TupleExpr *clone() const { return new TupleExpr( *this ); }
470 virtual void accept( Visitor &v ) { v.visit( this ); }
471 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
472 virtual void print( std::ostream &os, int indent = 0 ) const;
473 private:
474 std::list<Expression*> exprs;
475};
476
477/// SolvedTupleExpr represents a TupleExpr whose components have been type-resolved. It is effectively a shell for the code generator to work on
478class SolvedTupleExpr : public Expression {
479 public:
480 SolvedTupleExpr( Expression *_aname = 0 ) : Expression( _aname ) {}
481 SolvedTupleExpr( std::list<Expression *> &, Expression *_aname = 0 );
482 SolvedTupleExpr( const SolvedTupleExpr &other );
483 virtual ~SolvedTupleExpr() {}
484
485 std::list<Expression*> &get_exprs() { return exprs; }
486
487 virtual SolvedTupleExpr *clone() const { return new SolvedTupleExpr( *this ); }
488 virtual void accept( Visitor &v ) { v.visit( this ); }
489 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
490 virtual void print( std::ostream &os, int indent = 0 ) const;
491 private:
492 std::list<Expression*> exprs;
493};
494
495/// TypeExpr represents a type used in an expression (e.g. as a type generator parameter)
496class TypeExpr : public Expression {
497 public:
498 TypeExpr( Type *type );
499 TypeExpr( const TypeExpr &other );
500 virtual ~TypeExpr();
501
502 Type *get_type() const { return type; }
503 void set_type( Type *newValue ) { type = newValue; }
504
505 virtual TypeExpr *clone() const { return new TypeExpr( *this ); }
506 virtual void accept( Visitor &v ) { v.visit( this ); }
507 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
508 virtual void print( std::ostream &os, int indent = 0 ) const;
509 private:
510 Type *type;
511};
512
513/// AsmExpr represents a GCC 'asm constraint operand' used in an asm statement: [output] "=f" (result)
514class AsmExpr : public Expression {
515 public:
516 AsmExpr( Expression *inout, ConstantExpr *constraint, Expression *operand ) : inout( inout ), constraint( constraint ), operand( operand ) {}
517 virtual ~AsmExpr() { delete inout; delete constraint; delete operand; };
518
519 Expression *get_inout() const { return inout; }
520 void set_inout( Expression *newValue ) { inout = newValue; }
521
522 ConstantExpr *get_constraint() const { return constraint; }
523 void set_constraint( ConstantExpr *newValue ) { constraint = newValue; }
524
525 Expression *get_operand() const { return operand; }
526 void set_operand( Expression *newValue ) { operand = newValue; }
527
528 virtual AsmExpr *clone() const { return new AsmExpr( *this ); }
529 virtual void accept( Visitor &v ) { v.visit( this ); }
530 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
531 virtual void print( std::ostream &os, int indent = 0 ) const;
532 private:
533 // https://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Machine-Constraints.html#Machine-Constraints
534 Expression *inout;
535 ConstantExpr *constraint;
536 Expression *operand;
537};
538
539/// ValofExpr represents a GCC 'lambda expression'
540class UntypedValofExpr : public Expression {
541 public:
542 UntypedValofExpr( Statement *_body, Expression *_aname = 0 ) : Expression( _aname ), body ( _body ) {}
543 virtual ~UntypedValofExpr() {}
544
545 Expression *get_value();
546 Statement *get_body() const { return body; }
547
548 virtual UntypedValofExpr *clone() const { return new UntypedValofExpr( *this ); }
549 virtual void accept( Visitor &v ) { v.visit( this ); }
550 virtual Expression *acceptMutator( Mutator &m ) { return m.mutate( this ); }
551 virtual void print( std::ostream &os, int indent = 0 ) const;
552 private:
553 Statement *body;
554};
555
556std::ostream & operator<<( std::ostream & out, Expression * expr );
557
558#endif // EXPRESSION_H
559
560// Local Variables: //
561// tab-width: 4 //
562// mode: c++ //
563// compile-command: "make install" //
564// End: //
Note: See TracBrowser for help on using the repository browser.